diff --git a/.env.example b/.env.example
index 8db744c11..2275fa9cb 100644
--- a/.env.example
+++ b/.env.example
@@ -47,6 +47,7 @@ GITHUB_REDIRECT_URL=REDIRECT_URL
# Disable Chrome's Sandbox feature
# More information: https://github.com/stefanzweifel/screeenly/issues/174#issuecomment-423438612
SCREEENLY_DISABLE_SANDBOX=false
+SCREEENLY_DISK=public
## Misc Configuration
diff --git a/Procfile b/Procfile
new file mode 100644
index 000000000..48aab522a
--- /dev/null
+++ b/Procfile
@@ -0,0 +1 @@
+web: vendor/bin/heroku-php-apache2 public/
diff --git a/app.json b/app.json
new file mode 100644
index 000000000..dd782ccb1
--- /dev/null
+++ b/app.json
@@ -0,0 +1,34 @@
+{
+ "name": "screeenly",
+ "description": "Create screenshots of websites through a simple API",
+ "keywords": [
+ "screeenly",
+ "Laravel",
+ "PHP",
+ "screenshots",
+ "puppeteer"
+ ],
+ "website": "http://screeenly.com/",
+ "repository": "https://github.com/stefanzweifel/screeenly",
+ "success_url": "/",
+ "addons": [
+ "scheduler",
+ {
+ "plan": "heroku-postgresql",
+ "options": {
+ "version": "13"
+ }
+ }
+ ],
+ "buildpacks": [
+ {
+ "url": "heroku/php"
+ },
+ {
+ "url": "heroku/nodejs"
+ },
+ {
+ "url": "https://github.com/jontewks/puppeteer-heroku-buildpack"
+ }
+ ]
+}
diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php
index 14befceb0..d04f4a9f4 100644
--- a/app/Http/Middleware/TrustProxies.php
+++ b/app/Http/Middleware/TrustProxies.php
@@ -12,12 +12,12 @@ class TrustProxies extends Middleware
*
* @var array|string|null
*/
- protected $proxies;
+ protected $proxies = '*';
/**
* The headers that should be used to detect proxies.
*
* @var int
*/
- protected $headers = Request::HEADER_X_FORWARDED_ALL;
+ protected $headers = Request::HEADER_X_FORWARDED_AWS_ELB;
}
diff --git a/composer.json b/composer.json
index c275a78ba..dfa5e9780 100644
--- a/composer.json
+++ b/composer.json
@@ -17,6 +17,7 @@
"laravel/socialite": "^5.0",
"laravel/tinker": "^2.5",
"laravel/ui": "^3.0",
+ "league/flysystem-aws-s3-v3": "^1.0",
"sentry/sentry-laravel": "^2.1",
"spatie/browsershot": "^3.40"
},
diff --git a/composer.lock b/composer.lock
index d796d1940..a968b5ea8 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "3bad33094f75448fa917e4b817067e7d",
+ "content-hash": "421390706d1c7b0312d5713174a7b70f",
"packages": [
{
"name": "asm89/stack-cors",
@@ -62,6 +62,96 @@
},
"time": "2020-10-29T16:03:21+00:00"
},
+ {
+ "name": "aws/aws-sdk-php",
+ "version": "3.171.20",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/aws/aws-sdk-php.git",
+ "reference": "02aaf7007c5678a6358ea924cd85531300aa1747"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/02aaf7007c5678a6358ea924cd85531300aa1747",
+ "reference": "02aaf7007c5678a6358ea924cd85531300aa1747",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-pcre": "*",
+ "ext-simplexml": "*",
+ "guzzlehttp/guzzle": "^5.3.3|^6.2.1|^7.0",
+ "guzzlehttp/promises": "^1.0",
+ "guzzlehttp/psr7": "^1.4.1",
+ "mtdowling/jmespath.php": "^2.5",
+ "php": ">=5.5"
+ },
+ "require-dev": {
+ "andrewsville/php-token-reflection": "^1.4",
+ "aws/aws-php-sns-message-validator": "~1.0",
+ "behat/behat": "~3.0",
+ "doctrine/cache": "~1.4",
+ "ext-dom": "*",
+ "ext-openssl": "*",
+ "ext-pcntl": "*",
+ "ext-sockets": "*",
+ "nette/neon": "^2.3",
+ "paragonie/random_compat": ">= 2",
+ "phpunit/phpunit": "^4.8.35|^5.4.3",
+ "psr/cache": "^1.0",
+ "psr/simple-cache": "^1.0",
+ "sebastian/comparator": "^1.2.3"
+ },
+ "suggest": {
+ "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications",
+ "doctrine/cache": "To use the DoctrineCacheAdapter",
+ "ext-curl": "To send requests using cURL",
+ "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages",
+ "ext-sockets": "To use client-side monitoring"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Aws\\": "src/"
+ },
+ "files": [
+ "src/functions.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Amazon Web Services",
+ "homepage": "http://aws.amazon.com"
+ }
+ ],
+ "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project",
+ "homepage": "http://aws.amazon.com/sdkforphp",
+ "keywords": [
+ "amazon",
+ "aws",
+ "cloud",
+ "dynamodb",
+ "ec2",
+ "glacier",
+ "s3",
+ "sdk"
+ ],
+ "support": {
+ "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
+ "issues": "https://github.com/aws/aws-sdk-php/issues",
+ "source": "https://github.com/aws/aws-sdk-php/tree/3.171.20"
+ },
+ "time": "2021-01-19T19:13:08+00:00"
+ },
{
"name": "brick/math",
"version": "0.9.2",
@@ -2077,6 +2167,57 @@
],
"time": "2020-08-23T07:39:11+00:00"
},
+ {
+ "name": "league/flysystem-aws-s3-v3",
+ "version": "1.0.29",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git",
+ "reference": "4e25cc0582a36a786c31115e419c6e40498f6972"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/4e25cc0582a36a786c31115e419c6e40498f6972",
+ "reference": "4e25cc0582a36a786c31115e419c6e40498f6972",
+ "shasum": ""
+ },
+ "require": {
+ "aws/aws-sdk-php": "^3.20.0",
+ "league/flysystem": "^1.0.40",
+ "php": ">=5.5.0"
+ },
+ "require-dev": {
+ "henrikbjorn/phpspec-code-coverage": "~1.0.1",
+ "phpspec/phpspec": "^2.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Flysystem\\AwsS3v3\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frenky.net"
+ }
+ ],
+ "description": "Flysystem adapter for the AWS S3 SDK v3.x",
+ "support": {
+ "issues": "https://github.com/thephpleague/flysystem-aws-s3-v3/issues",
+ "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/1.0.29"
+ },
+ "time": "2020-10-08T18:58:37+00:00"
+ },
{
"name": "league/glide",
"version": "1.7.0",
@@ -2369,6 +2510,67 @@
],
"time": "2020-12-14T13:15:25+00:00"
},
+ {
+ "name": "mtdowling/jmespath.php",
+ "version": "2.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/jmespath/jmespath.php.git",
+ "reference": "42dae2cbd13154083ca6d70099692fef8ca84bfb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/42dae2cbd13154083ca6d70099692fef8ca84bfb",
+ "reference": "42dae2cbd13154083ca6d70099692fef8ca84bfb",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.4 || ^7.0 || ^8.0",
+ "symfony/polyfill-mbstring": "^1.17"
+ },
+ "require-dev": {
+ "composer/xdebug-handler": "^1.4",
+ "phpunit/phpunit": "^4.8.36 || ^7.5.15"
+ },
+ "bin": [
+ "bin/jp.php"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.6-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "JmesPath\\": "src/"
+ },
+ "files": [
+ "src/JmesPath.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "description": "Declaratively specify how to extract elements from a JSON document",
+ "keywords": [
+ "json",
+ "jsonpath"
+ ],
+ "support": {
+ "issues": "https://github.com/jmespath/jmespath.php/issues",
+ "source": "https://github.com/jmespath/jmespath.php/tree/2.6.0"
+ },
+ "time": "2020-07-31T21:01:56+00:00"
+ },
{
"name": "nesbot/carbon",
"version": "2.44.0",
diff --git a/config/filesystems.php b/config/filesystems.php
index 6c39a0162..323674fa0 100644
--- a/config/filesystems.php
+++ b/config/filesystems.php
@@ -55,6 +55,7 @@
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
+ 'visibility' => 'public',
],
],
diff --git a/config/logging.php b/config/logging.php
index 6e536a80f..0b30dc72d 100644
--- a/config/logging.php
+++ b/config/logging.php
@@ -37,7 +37,7 @@
'channels' => [
'stack' => [
'driver' => 'stack',
- 'channels' => ['daily'],
+ 'channels' => ['daily', 'errorlog'],
'ignore_exceptions' => false,
],
diff --git a/config/screeenly.php b/config/screeenly.php
index 39d4c5e7b..1a6e6d104 100644
--- a/config/screeenly.php
+++ b/config/screeenly.php
@@ -8,4 +8,9 @@
*/
'disable_sandbox' => env('SCREEENLY_DISABLE_SANDBOX', false),
+ /**
+ * The Filesystem disk where screenshots are being stored
+ */
+ 'filesystem_disk' => env('SCREEENLY_DISK', 'public'),
+
];
diff --git a/database/migrations/2021_01_20_193343_create_sessions_table.php b/database/migrations/2021_01_20_193343_create_sessions_table.php
new file mode 100644
index 000000000..88b4a316e
--- /dev/null
+++ b/database/migrations/2021_01_20_193343_create_sessions_table.php
@@ -0,0 +1,35 @@
+string('id')->primary();
+ $table->foreignId('user_id')->nullable()->index();
+ $table->string('ip_address', 45)->nullable();
+ $table->text('user_agent')->nullable();
+ $table->text('payload');
+ $table->integer('last_activity')->index();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('sessions');
+ }
+}
diff --git a/modules/Screeenly/Entities/Screenshot.php b/modules/Screeenly/Entities/Screenshot.php
index baa947f07..181cb5408 100644
--- a/modules/Screeenly/Entities/Screenshot.php
+++ b/modules/Screeenly/Entities/Screenshot.php
@@ -27,13 +27,18 @@ class Screenshot
*/
protected $publicUrl;
+ /**
+ * Screenshot constructor.
+ * @param $absolutePath
+ * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
+ */
public function __construct($absolutePath)
{
$this->doesScreenshotExist($absolutePath);
$this->path = $absolutePath;
$this->filename = basename($absolutePath);
- $this->publicUrl = asset(Storage::url($this->filename));
- $this->base64 = base64_encode(Storage::disk('public')->get($this->filename));
+ $this->publicUrl = asset(Storage::disk(config('screeenly.filesystem_disk'))->url($this->filename));
+ $this->base64 = base64_encode(Storage::disk(config('screeenly.filesystem_disk'))->get($this->filename));
}
/**
@@ -54,6 +59,9 @@ public function getFilename()
return $this->filename;
}
+ /**
+ * @return string
+ */
public function getPath()
{
return $this->path;
@@ -70,13 +78,20 @@ public function getPublicUrl()
/**
* Test if a file is available.
- * @param string $absolutePath
+ * @param string $absolutePath
* @return void
+ * @throws Exception
*/
- protected function doesScreenshotExist($absolutePath)
+ protected function doesScreenshotExist(string $absolutePath)
{
- if (file_exists($absolutePath) == false) {
- throw new Exception("Screenshot can't be generated for given URL");
+ if (config('screeenly.filesystem_disk') == 'public') {
+ if (file_exists($absolutePath) == false) {
+ throw new Exception("Screenshot can't be generated for given URL");
+ }
+ } else {
+ if (Storage::disk(config('screeenly.filesystem_disk'))->exists($absolutePath) == false) {
+ throw new Exception("Screenshot can't be generated for given URL");
+ }
}
}
@@ -86,6 +101,6 @@ protected function doesScreenshotExist($absolutePath)
*/
public function delete()
{
- return Storage::disk('public')->delete($this->filename);
+ return Storage::disk(config('screeenly.filesystem_disk'))->delete($this->filename);
}
}
diff --git a/modules/Screeenly/Http/routes/console.php b/modules/Screeenly/Http/routes/console.php
index 816fd7be9..5950557a5 100644
--- a/modules/Screeenly/Http/routes/console.php
+++ b/modules/Screeenly/Http/routes/console.php
@@ -4,12 +4,17 @@
use Screeenly\Models\ApiLog;
Artisan::command('screeenly:cleanup', function () {
- ApiLog::where('created_at', '<', Carbon::now()->subHours(1))->get()->each(function ($log) {
- try {
- $log->screenshot()->delete();
- $log->delete();
- } catch (Exception $e) {
- $log->delete();
- }
- });
+ ApiLog::query()
+ ->where('created_at', '<', Carbon::now()->subHours(1))
+ ->get()
+ ->each(function (ApiLog $log) {
+ $this->info("Delete Log #{$log->id}");
+
+ try {
+ $log->screenshot()->delete();
+ $log->delete();
+ } catch (Exception $e) {
+ // $log->delete();
+ }
+ });
})->describe('Delete Screenshot Files older than 1 hour.');
diff --git a/modules/Screeenly/Services/ChromeBrowser.php b/modules/Screeenly/Services/ChromeBrowser.php
index fba9654b4..3263c6312 100644
--- a/modules/Screeenly/Services/ChromeBrowser.php
+++ b/modules/Screeenly/Services/ChromeBrowser.php
@@ -28,9 +28,9 @@ public function capture(Url $url, $filename)
$browser->fullPage();
}
- Storage::disk('public')->put($filename, $browser->screenshot());
+ Storage::disk(config('screeenly.filesystem_disk'))->put($filename, $browser->screenshot());
- $path = Storage::disk('public')->path($filename);
+ $path = Storage::disk(config('screeenly.filesystem_disk'))->path($filename);
return new Screenshot($path);
}
diff --git a/phpunit.xml b/phpunit.xml
index f3e7b9577..cab30f2c3 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -25,5 +25,6 @@
+
diff --git a/readme.md b/readme.md
index f7867688f..8c8ffb44c 100644
--- a/readme.md
+++ b/readme.md
@@ -33,17 +33,30 @@ The repository itself will soon get updates for Laravel 7 and Laravel 8.
---
-
-
-
## Documentation and more
The [wiki](https://github.com/stefanzweifel/screeenly/wiki) holds the documentation.
- [API specification](https://github.com/stefanzweifel/screeenly/wiki/Use-the-API)
-- [Self Hosting Guide](https://github.com/stefanzweifel/screeenly/wiki/Requirements-and-Install)
- [Read about the code structure](https://github.com/stefanzweifel/screeenly/wiki/Read-the-Code)
+
+## Self Hosting
+
+screeenly is quite a simple PHP app. Therefore, it's quite easy to self host the application on your own server.
+
+### Self Hosting on your own Server
+
+If you're comfortable running your own server follow your self-hosting guide [here](https://github.com/stefanzweifel/screeenly/wiki/Requirements-and-Install).
+
+### Deploy to Heroku
+
+[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/stefanzweifel/screeenly/tree/master)
+
+
+If managing servers is not your thing, we've also written [a guide](https://github.com/stefanzweifel/screeenly/wiki/Deploy-to-Heroku) on how to deploy the app to Heroku.
+By using Heroku, you can run your own version of screeenly basically for free.
+
## Docker Images
If you're interested in a Docker version of screeenly, you can use the daily built images created by [Jacek Szafarkiewicz](https://github.com/hadogenes).
diff --git a/tests/modules/Screeenly/integration/api/v1/ApiV1ScreenshotTest.php b/tests/modules/Screeenly/integration/api/v1/ApiV1ScreenshotTest.php
index 24fe05f04..b6376124f 100644
--- a/tests/modules/Screeenly/integration/api/v1/ApiV1ScreenshotTest.php
+++ b/tests/modules/Screeenly/integration/api/v1/ApiV1ScreenshotTest.php
@@ -132,9 +132,9 @@ public function it_returns_an_errof_if_url_has_no_protocol_prefix()
/** @test */
public function it_returns_path_and_base64_representation_of_to_image_on_successful_request()
{
- Storage::fake('public');
+ Storage::fake(config('screeenly.filesystem_disk'));
- Storage::disk('public')
+ Storage::disk(config('screeenly.filesystem_disk'))
->put(
'test-screenshot.jpg',
file_get_contents(storage_path('testing/test-screenshot.jpg'))
diff --git a/tests/modules/Screeenly/integration/api/v2/ApiV2ScreenshotTest.php b/tests/modules/Screeenly/integration/api/v2/ApiV2ScreenshotTest.php
index 3c3bb5e9d..fd93dae08 100644
--- a/tests/modules/Screeenly/integration/api/v2/ApiV2ScreenshotTest.php
+++ b/tests/modules/Screeenly/integration/api/v2/ApiV2ScreenshotTest.php
@@ -50,9 +50,9 @@ public function it_shows_error_if_not_a_url_is_passed()
/** @test */
public function it_returns_base64_representation_of_screenshot()
{
- Storage::fake('public');
+ Storage::fake(config('screeenly.filesystem_disk'));
- Storage::disk('public')
+ Storage::disk(config('screeenly.filesystem_disk'))
->put(
'test-screenshot.jpg',
file_get_contents(storage_path('testing/test-screenshot.jpg'))
diff --git a/tests/modules/Screeenly/unit/Entities/ScreenshotTest.php b/tests/modules/Screeenly/unit/Entities/ScreenshotTest.php
index 3b08ebb14..cafd7f7c2 100644
--- a/tests/modules/Screeenly/unit/Entities/ScreenshotTest.php
+++ b/tests/modules/Screeenly/unit/Entities/ScreenshotTest.php
@@ -8,7 +8,7 @@ protected function setUp(): void
{
parent::setUp();
- Storage::disk('public')->put('test-screenshot.jpg', file_get_contents(storage_path('testing/test-screenshot.jpg')));
+ Storage::disk(config('screeenly.filesystem_disk'))->put('test-screenshot.jpg', file_get_contents(storage_path('testing/test-screenshot.jpg')));
}
/** @test */
@@ -46,7 +46,7 @@ public function it_throws_exception_if_screenshot_is_not_available()
/** @test */
public function it_deletes_screenshot_from_disk()
{
- Storage::disk('public')->delete('test-screenshot.jpg');
+ Storage::disk(config('screeenly.filesystem_disk'))->delete('test-screenshot.jpg');
Storage::put(
'test-screenshot-to-delete.jpg',
@@ -58,6 +58,6 @@ public function it_deletes_screenshot_from_disk()
$this->assertTrue($screenshot->delete());
- Storage::disk('public')->assertMissing('test-screenshot-to-delete.jpg');
+ Storage::disk(config('screeenly.filesystem_disk'))->assertMissing('test-screenshot-to-delete.jpg');
}
}
diff --git a/tests/modules/Screeenly/unit/Services/CaptureServiceTest.php b/tests/modules/Screeenly/unit/Services/CaptureServiceTest.php
index 95574158f..2c2f67bb0 100644
--- a/tests/modules/Screeenly/unit/Services/CaptureServiceTest.php
+++ b/tests/modules/Screeenly/unit/Services/CaptureServiceTest.php
@@ -54,9 +54,9 @@ public function it_lets_you_define_a_delay()
/** @test */
public function it_captures_screenshot_and_returns_screenshot_instance()
{
- Storage::fake('public');
+ Storage::fake(config('screeenly.filesystem_disk'));
- Storage::disk('public')
+ Storage::disk(config('screeenly.filesystem_disk'))
->put(
'test-screenshot.jpg',
file_get_contents(storage_path('testing/test-screenshot.jpg'))