Merge pull request #4754 from coollabsio/improve-git-and-service-provider
Improves: GitHub handling, AppServiceProvider and 500 error message rendering
This commit is contained in:
@@ -27,19 +27,28 @@ class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$github_access_token = generate_github_jwt_token($this->github_app);
|
$github_access_token = generateGithubJwt($this->github_app);
|
||||||
|
|
||||||
$response = Http::withHeaders([
|
$response = Http::withHeaders([
|
||||||
'Authorization' => "Bearer $github_access_token",
|
'Authorization' => "Bearer $github_access_token",
|
||||||
'Accept' => 'application/vnd.github+json',
|
'Accept' => 'application/vnd.github+json',
|
||||||
])->get("{$this->github_app->api_url}/app");
|
])->get("{$this->github_app->api_url}/app");
|
||||||
|
|
||||||
|
if (! $response->successful()) {
|
||||||
|
throw new \RuntimeException('Failed to fetch GitHub app permissions: '.$response->body());
|
||||||
|
}
|
||||||
|
|
||||||
$response = $response->json();
|
$response = $response->json();
|
||||||
$permissions = data_get($response, 'permissions');
|
$permissions = data_get($response, 'permissions');
|
||||||
|
|
||||||
$this->github_app->contents = data_get($permissions, 'contents');
|
$this->github_app->contents = data_get($permissions, 'contents');
|
||||||
$this->github_app->metadata = data_get($permissions, 'metadata');
|
$this->github_app->metadata = data_get($permissions, 'metadata');
|
||||||
$this->github_app->pull_requests = data_get($permissions, 'pull_requests');
|
$this->github_app->pull_requests = data_get($permissions, 'pull_requests');
|
||||||
$this->github_app->administration = data_get($permissions, 'administration');
|
$this->github_app->administration = data_get($permissions, 'administration');
|
||||||
|
|
||||||
$this->github_app->save();
|
$this->github_app->save();
|
||||||
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
|
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
|
||||||
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('GithubAppPermissionJob failed with: '.$e->getMessage());
|
send_internal_notification('GithubAppPermissionJob failed with: '.$e->getMessage());
|
||||||
throw $e;
|
throw $e;
|
||||||
|
@@ -105,7 +105,7 @@ class GithubPrivateRepository extends Component
|
|||||||
$this->page = 1;
|
$this->page = 1;
|
||||||
$this->selected_github_app_id = $github_app_id;
|
$this->selected_github_app_id = $github_app_id;
|
||||||
$this->github_app = GithubApp::where('id', $github_app_id)->first();
|
$this->github_app = GithubApp::where('id', $github_app_id)->first();
|
||||||
$this->token = generate_github_installation_token($this->github_app);
|
$this->token = generateGithubInstallationToken($this->github_app);
|
||||||
$this->loadRepositoryByPage();
|
$this->loadRepositoryByPage();
|
||||||
if ($this->repositories->count() < $this->total_repositories_count) {
|
if ($this->repositories->count() < $this->total_repositories_count) {
|
||||||
while ($this->repositories->count() < $this->total_repositories_count) {
|
while ($this->repositories->count() < $this->total_repositories_count) {
|
||||||
|
@@ -76,7 +76,7 @@ class Change extends Component
|
|||||||
// Need administration:read:write permission
|
// Need administration:read:write permission
|
||||||
// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#list-self-hosted-runners-for-a-repository
|
// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#list-self-hosted-runners-for-a-repository
|
||||||
|
|
||||||
// $github_access_token = generate_github_installation_token($this->github_app);
|
// $github_access_token = generateGithubInstallationToken($this->github_app);
|
||||||
// $repositories = Http::withToken($github_access_token)->get("{$this->github_app->api_url}/installation/repositories?per_page=100");
|
// $repositories = Http::withToken($github_access_token)->get("{$this->github_app->api_url}/installation/repositories?per_page=100");
|
||||||
// $runners_by_repository = collect([]);
|
// $runners_by_repository = collect([]);
|
||||||
// $repositories = $repositories->json()['repositories'];
|
// $repositories = $repositories->json()['repositories'];
|
||||||
|
@@ -999,7 +999,7 @@ class Application extends BaseModel
|
|||||||
$fullRepoUrl = "{$this->source->html_url}/{$customRepository}";
|
$fullRepoUrl = "{$this->source->html_url}/{$customRepository}";
|
||||||
$base_command = "{$base_command} {$this->source->html_url}/{$customRepository}";
|
$base_command = "{$base_command} {$this->source->html_url}/{$customRepository}";
|
||||||
} else {
|
} else {
|
||||||
$github_access_token = generate_github_installation_token($this->source);
|
$github_access_token = generateGithubInstallationToken($this->source);
|
||||||
|
|
||||||
if ($exec_in_docker) {
|
if ($exec_in_docker) {
|
||||||
$base_command = "{$base_command} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}.git";
|
$base_command = "{$base_command} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}.git";
|
||||||
@@ -1111,7 +1111,7 @@ class Application extends BaseModel
|
|||||||
$commands->push($git_clone_command);
|
$commands->push($git_clone_command);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$github_access_token = generate_github_installation_token($this->source);
|
$github_access_token = generateGithubInstallationToken($this->source);
|
||||||
if ($exec_in_docker) {
|
if ($exec_in_docker) {
|
||||||
$git_clone_command = "{$git_clone_command} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}.git {$baseDir}";
|
$git_clone_command = "{$git_clone_command} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}.git {$baseDir}";
|
||||||
$fullRepoUrl = "$source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}.git";
|
$fullRepoUrl = "$source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}.git";
|
||||||
|
@@ -3,37 +3,68 @@
|
|||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
use App\Models\PersonalAccessToken;
|
use App\Models\PersonalAccessToken;
|
||||||
use Illuminate\Support\Facades\Event;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Facades\App;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Illuminate\Validation\Rules\Password;
|
use Illuminate\Validation\Rules\Password;
|
||||||
use Laravel\Sanctum\Sanctum;
|
use Laravel\Sanctum\Sanctum;
|
||||||
|
use Laravel\Telescope\TelescopeServiceProvider;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
public function register(): void
|
public function register(): void
|
||||||
{
|
{
|
||||||
if ($this->app->environment('local')) {
|
if (App::isLocal()) {
|
||||||
$this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class);
|
$this->app->register(TelescopeServiceProvider::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) {
|
$this->configureCommands();
|
||||||
$event->extendSocialite('authentik', \SocialiteProviders\Authentik\Provider::class);
|
$this->configureModels();
|
||||||
});
|
$this->configurePasswords();
|
||||||
Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
|
$this->configureSanctumModel();
|
||||||
|
$this->configureGitHubHttp();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function configureCommands(): void
|
||||||
|
{
|
||||||
|
if (App::isProduction()) {
|
||||||
|
DB::prohibitDestructiveCommands();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function configureModels(): void
|
||||||
|
{
|
||||||
|
// Disabled because it's causing issues with the application
|
||||||
|
// Model::shouldBeStrict();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function configurePasswords(): void
|
||||||
|
{
|
||||||
Password::defaults(function () {
|
Password::defaults(function () {
|
||||||
$rule = Password::min(8);
|
return App::isProduction()
|
||||||
|
? Password::min(8)
|
||||||
return $this->app->isProduction()
|
->mixedCase()
|
||||||
? $rule->mixedCase()->letters()->numbers()->symbols()
|
->letters()
|
||||||
: $rule;
|
->numbers()
|
||||||
|
->symbols()
|
||||||
|
->uncompromised()
|
||||||
|
: Password::min(8)->letters();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Http::macro('github', function (string $api_url, ?string $github_access_token = null) {
|
private function configureSanctumModel(): void
|
||||||
|
{
|
||||||
|
Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function configureGitHubHttp(): void
|
||||||
|
{
|
||||||
|
Http::macro('GitHub', function (string $api_url, ?string $github_access_token = null) {
|
||||||
if ($github_access_token) {
|
if ($github_access_token) {
|
||||||
return Http::withHeaders([
|
return Http::withHeaders([
|
||||||
'X-GitHub-Api-Version' => '2022-11-28',
|
'X-GitHub-Api-Version' => '2022-11-28',
|
||||||
|
@@ -12,77 +12,108 @@ use Lcobucci\JWT\Signer\Key\InMemory;
|
|||||||
use Lcobucci\JWT\Signer\Rsa\Sha256;
|
use Lcobucci\JWT\Signer\Rsa\Sha256;
|
||||||
use Lcobucci\JWT\Token\Builder;
|
use Lcobucci\JWT\Token\Builder;
|
||||||
|
|
||||||
function generate_github_installation_token(GithubApp $source)
|
function generateGithubToken(GithubApp $source, string $type)
|
||||||
{
|
{
|
||||||
$signingKey = InMemory::plainText($source->privateKey->private_key);
|
$response = Http::get("{$source->api_url}/zen");
|
||||||
$algorithm = new Sha256;
|
$serverTime = CarbonImmutable::now()->setTimezone('UTC');
|
||||||
$tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default()));
|
$githubTime = Carbon::parse($response->header('date'));
|
||||||
$now = CarbonImmutable::now();
|
$timeDiff = abs($serverTime->diffInSeconds($githubTime));
|
||||||
$now = $now->setTime($now->format('H'), $now->format('i'));
|
|
||||||
$issuedToken = $tokenBuilder
|
if ($timeDiff > 50) {
|
||||||
->issuedBy($source->app_id)
|
throw new \Exception(
|
||||||
->issuedAt($now)
|
'System time is out of sync with GitHub API time:<br>'.
|
||||||
->expiresAt($now->modify('+10 minutes'))
|
'- System time: '.$serverTime->format('Y-m-d H:i:s').' UTC<br>'.
|
||||||
->getToken($algorithm, $signingKey)
|
'- GitHub time: '.$githubTime->format('Y-m-d H:i:s').' UTC<br>'.
|
||||||
->toString();
|
'- Difference: '.$timeDiff.' seconds<br>'.
|
||||||
$token = Http::withHeaders([
|
'Please synchronize your system clock.'
|
||||||
'Authorization' => "Bearer $issuedToken",
|
);
|
||||||
'Accept' => 'application/vnd.github.machine-man-preview+json',
|
|
||||||
])->post("{$source->api_url}/app/installations/{$source->installation_id}/access_tokens");
|
|
||||||
if ($token->failed()) {
|
|
||||||
throw new RuntimeException('Failed to get access token for '.$source->name.' with error: '.data_get($token->json(), 'message', 'no error message found'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $token->json()['token'];
|
|
||||||
}
|
|
||||||
|
|
||||||
function generate_github_jwt_token(GithubApp $source)
|
|
||||||
{
|
|
||||||
$signingKey = InMemory::plainText($source->privateKey->private_key);
|
$signingKey = InMemory::plainText($source->privateKey->private_key);
|
||||||
$algorithm = new Sha256;
|
$algorithm = new Sha256;
|
||||||
$tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default()));
|
$tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default()));
|
||||||
$now = CarbonImmutable::now();
|
$now = CarbonImmutable::now()->setTimezone('UTC');
|
||||||
$now = $now->setTime($now->format('H'), $now->format('i'));
|
$now = $now->setTime($now->format('H'), $now->format('i'), $now->format('s'));
|
||||||
|
|
||||||
return $tokenBuilder
|
$jwt = $tokenBuilder
|
||||||
->issuedBy($source->app_id)
|
->issuedBy($source->app_id)
|
||||||
->issuedAt($now->modify('-1 minute'))
|
->issuedAt($now->modify('-1 minute'))
|
||||||
->expiresAt($now->modify('+10 minutes'))
|
->expiresAt($now->modify('+8 minutes'))
|
||||||
->getToken($algorithm, $signingKey)
|
->getToken($algorithm, $signingKey)
|
||||||
->toString();
|
->toString();
|
||||||
|
|
||||||
|
return match ($type) {
|
||||||
|
'jwt' => $jwt,
|
||||||
|
'installation' => (function () use ($source, $jwt) {
|
||||||
|
$response = Http::withHeaders([
|
||||||
|
'Authorization' => "Bearer $jwt",
|
||||||
|
'Accept' => 'application/vnd.github.machine-man-preview+json',
|
||||||
|
])->post("{$source->api_url}/app/installations/{$source->installation_id}/access_tokens");
|
||||||
|
|
||||||
|
if (! $response->successful()) {
|
||||||
|
$error = data_get($response->json(), 'message', 'no error message found');
|
||||||
|
throw new RuntimeException("Failed to get installation token for {$source->name} with error: ".$error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response->json()['token'];
|
||||||
|
})(),
|
||||||
|
default => throw new \InvalidArgumentException("Unsupported token type: {$type}")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateGithubInstallationToken(GithubApp $source)
|
||||||
|
{
|
||||||
|
return generateGithubToken($source, 'installation');
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateGithubJwt(GithubApp $source)
|
||||||
|
{
|
||||||
|
return generateGithubToken($source, 'jwt');
|
||||||
}
|
}
|
||||||
|
|
||||||
function githubApi(GithubApp|GitlabApp|null $source, string $endpoint, string $method = 'get', ?array $data = null, bool $throwError = true)
|
function githubApi(GithubApp|GitlabApp|null $source, string $endpoint, string $method = 'get', ?array $data = null, bool $throwError = true)
|
||||||
{
|
{
|
||||||
if (is_null($source)) {
|
if (is_null($source)) {
|
||||||
throw new \Exception('Not implemented yet.');
|
throw new \Exception('Source is required for API calls');
|
||||||
}
|
}
|
||||||
if ($source->getMorphClass() === \App\Models\GithubApp::class) {
|
|
||||||
if ($source->is_public) {
|
if ($source->getMorphClass() !== GithubApp::class) {
|
||||||
$response = Http::github($source->api_url)->$method($endpoint);
|
throw new \InvalidArgumentException("Unsupported source type: {$source->getMorphClass()}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($source->is_public) {
|
||||||
|
$response = Http::GitHub($source->api_url)->$method($endpoint);
|
||||||
|
} else {
|
||||||
|
$token = generateGithubInstallationToken($source);
|
||||||
|
if ($data && in_array(strtolower($method), ['post', 'patch', 'put'])) {
|
||||||
|
$response = Http::GitHub($source->api_url, $token)->$method($endpoint, $data);
|
||||||
} else {
|
} else {
|
||||||
$github_access_token = generate_github_installation_token($source);
|
$response = Http::GitHub($source->api_url, $token)->$method($endpoint);
|
||||||
if ($data && ($method === 'post' || $method === 'patch' || $method === 'put')) {
|
|
||||||
$response = Http::github($source->api_url, $github_access_token)->$method($endpoint, $data);
|
|
||||||
} else {
|
|
||||||
$response = Http::github($source->api_url, $github_access_token)->$method($endpoint);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$json = $response->json();
|
|
||||||
if ($response->failed() && $throwError) {
|
if (! $response->successful() && $throwError) {
|
||||||
ray($json);
|
$resetTime = Carbon::parse((int) $response->header('X-RateLimit-Reset'))->format('Y-m-d H:i:s');
|
||||||
throw new \Exception("Failed to get data from {$source->name} with error:<br><br>".$json['message'].'<br><br>Rate Limit resets at: '.Carbon::parse((int) $response->header('X-RateLimit-Reset'))->format('Y-m-d H:i:s').'UTC');
|
$errorMessage = data_get($response->json(), 'message', 'no error message found');
|
||||||
|
$remainingCalls = $response->header('X-RateLimit-Remaining', '0');
|
||||||
|
|
||||||
|
throw new \Exception(
|
||||||
|
'GitHub API call failed:<br>'.
|
||||||
|
"Error: {$errorMessage}<br>".
|
||||||
|
'Rate Limit Status:<br>'.
|
||||||
|
"- Remaining Calls: {$remainingCalls}<br>".
|
||||||
|
"- Reset Time: {$resetTime} UTC"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'rate_limit_remaining' => $response->header('X-RateLimit-Remaining'),
|
'rate_limit_remaining' => $response->header('X-RateLimit-Remaining'),
|
||||||
'rate_limit_reset' => $response->header('X-RateLimit-Reset'),
|
'rate_limit_reset' => $response->header('X-RateLimit-Reset'),
|
||||||
'data' => collect($json),
|
'data' => collect($response->json()),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_installation_path(GithubApp $source)
|
function getInstallationPath(GithubApp $source)
|
||||||
{
|
{
|
||||||
$github = GithubApp::where('uuid', $source->uuid)->first();
|
$github = GithubApp::where('uuid', $source->uuid)->first();
|
||||||
$name = str(Str::kebab($github->name));
|
$name = str(Str::kebab($github->name));
|
||||||
@@ -90,7 +121,8 @@ function get_installation_path(GithubApp $source)
|
|||||||
|
|
||||||
return "$github->html_url/$installation_path/$name/installations/new";
|
return "$github->html_url/$installation_path/$name/installations/new";
|
||||||
}
|
}
|
||||||
function get_permissions_path(GithubApp $source)
|
|
||||||
|
function getPermissionsPath(GithubApp $source)
|
||||||
{
|
{
|
||||||
$github = GithubApp::where('uuid', $source->uuid)->first();
|
$github = GithubApp::where('uuid', $source->uuid)->first();
|
||||||
$name = str(Str::kebab($github->name));
|
$name = str(Str::kebab($github->name));
|
||||||
|
@@ -47,6 +47,7 @@
|
|||||||
"spatie/laravel-ray": "^1.37",
|
"spatie/laravel-ray": "^1.37",
|
||||||
"spatie/laravel-schemaless-attributes": "^2.4",
|
"spatie/laravel-schemaless-attributes": "^2.4",
|
||||||
"spatie/url": "^2.2",
|
"spatie/url": "^2.2",
|
||||||
|
"stevebauman/purify": "^6.2",
|
||||||
"stripe/stripe-php": "^16.2.0",
|
"stripe/stripe-php": "^16.2.0",
|
||||||
"symfony/yaml": "^7.1.6",
|
"symfony/yaml": "^7.1.6",
|
||||||
"visus/cuid2": "^4.1.0",
|
"visus/cuid2": "^4.1.0",
|
||||||
|
127
composer.lock
generated
127
composer.lock
generated
@@ -1883,6 +1883,67 @@
|
|||||||
],
|
],
|
||||||
"time": "2024-12-27T00:36:43+00:00"
|
"time": "2024-12-27T00:36:43+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "ezyang/htmlpurifier",
|
||||||
|
"version": "v4.18.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/ezyang/htmlpurifier.git",
|
||||||
|
"reference": "cb56001e54359df7ae76dc522d08845dc741621b"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/cb56001e54359df7ae76dc522d08845dc741621b",
|
||||||
|
"reference": "cb56001e54359df7ae76dc522d08845dc741621b",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"cerdic/css-tidy": "^1.7 || ^2.0",
|
||||||
|
"simpletest/simpletest": "dev-master"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.",
|
||||||
|
"ext-bcmath": "Used for unit conversion and imagecrash protection",
|
||||||
|
"ext-iconv": "Converts text to and from non-UTF-8 encodings",
|
||||||
|
"ext-tidy": "Used for pretty-printing HTML"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"library/HTMLPurifier.composer.php"
|
||||||
|
],
|
||||||
|
"psr-0": {
|
||||||
|
"HTMLPurifier": "library/"
|
||||||
|
},
|
||||||
|
"exclude-from-classmap": [
|
||||||
|
"/library/HTMLPurifier/Language/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"LGPL-2.1-or-later"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Edward Z. Yang",
|
||||||
|
"email": "admin@htmlpurifier.org",
|
||||||
|
"homepage": "http://ezyang.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Standards compliant HTML filter written in PHP",
|
||||||
|
"homepage": "http://htmlpurifier.org/",
|
||||||
|
"keywords": [
|
||||||
|
"html"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/ezyang/htmlpurifier/issues",
|
||||||
|
"source": "https://github.com/ezyang/htmlpurifier/tree/v4.18.0"
|
||||||
|
},
|
||||||
|
"time": "2024-11-01T03:51:45+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "firebase/php-jwt",
|
"name": "firebase/php-jwt",
|
||||||
"version": "v6.10.2",
|
"version": "v6.10.2",
|
||||||
@@ -8330,6 +8391,72 @@
|
|||||||
],
|
],
|
||||||
"time": "2024-03-08T11:35:19+00:00"
|
"time": "2024-03-08T11:35:19+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "stevebauman/purify",
|
||||||
|
"version": "v6.2.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/stevebauman/purify.git",
|
||||||
|
"reference": "a449299a3d5f5f8ef177e626721b3f69143890a4"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/stevebauman/purify/zipball/a449299a3d5f5f8ef177e626721b3f69143890a4",
|
||||||
|
"reference": "a449299a3d5f5f8ef177e626721b3f69143890a4",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ezyang/htmlpurifier": "^4.17",
|
||||||
|
"illuminate/contracts": "^7.0|^8.0|^9.0|^10.0|^11.0",
|
||||||
|
"illuminate/support": "^7.0|^8.0|^9.0|^10.0|^11.0",
|
||||||
|
"php": ">=7.4"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"orchestra/testbench": "^5.0|^6.0|^7.0|^8.0|^9.0",
|
||||||
|
"phpunit/phpunit": "^8.0|^9.0|^10.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"aliases": {
|
||||||
|
"Purify": "Stevebauman\\Purify\\Facades\\Purify"
|
||||||
|
},
|
||||||
|
"providers": [
|
||||||
|
"Stevebauman\\Purify\\PurifyServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Stevebauman\\Purify\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Steve Bauman",
|
||||||
|
"email": "steven_bauman@outlook.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "An HTML Purifier / Sanitizer for Laravel",
|
||||||
|
"keywords": [
|
||||||
|
"Purifier",
|
||||||
|
"clean",
|
||||||
|
"cleaner",
|
||||||
|
"html",
|
||||||
|
"laravel",
|
||||||
|
"purification",
|
||||||
|
"purify"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/stevebauman/purify/issues",
|
||||||
|
"source": "https://github.com/stevebauman/purify/tree/v6.2.2"
|
||||||
|
},
|
||||||
|
"time": "2024-09-24T12:27:10+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "stripe/stripe-php",
|
"name": "stripe/stripe-php",
|
||||||
"version": "v16.4.0",
|
"version": "v16.4.0",
|
||||||
|
115
config/purify.php
Normal file
115
config/purify.php
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Stevebauman\Purify\Definitions\Html5Definition;
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Default Config
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This option defines the default config that is provided to HTMLPurifier.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'default' => 'default',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Config sets
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may configure various sets of configuration for differentiated use of HTMLPurifier.
|
||||||
|
| A specific set of configuration can be applied by calling the "config($name)" method on
|
||||||
|
| a Purify instance. Feel free to add/remove/customize these attributes as you wish.
|
||||||
|
|
|
||||||
|
| Documentation: http://htmlpurifier.org/live/configdoc/plain.html
|
||||||
|
|
|
||||||
|
| Core.Encoding The encoding to convert input to.
|
||||||
|
| HTML.Doctype Doctype to use during filtering.
|
||||||
|
| HTML.Allowed The allowed HTML Elements with their allowed attributes.
|
||||||
|
| HTML.ForbiddenElements The forbidden HTML elements. Elements that are listed in this
|
||||||
|
| string will be removed, however their content will remain.
|
||||||
|
| CSS.AllowedProperties The Allowed CSS properties.
|
||||||
|
| AutoFormat.AutoParagraph Newlines are converted in to paragraphs whenever possible.
|
||||||
|
| AutoFormat.RemoveEmpty Remove empty elements that contribute no semantic information to the document.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'configs' => [
|
||||||
|
|
||||||
|
'default' => [
|
||||||
|
'Core.Encoding' => 'utf-8',
|
||||||
|
'HTML.Doctype' => 'HTML 4.01 Transitional',
|
||||||
|
'HTML.Allowed' => 'h1,h2,h3,h4,h5,h6,b,u,strong,i,em,s,del,a[href|title],ul,ol,li,p[style],br,span,img[width|height|alt|src],blockquote',
|
||||||
|
'HTML.ForbiddenElements' => '',
|
||||||
|
'CSS.AllowedProperties' => 'font,font-size,font-weight,font-style,font-family,text-decoration,padding-left,color,background-color,text-align',
|
||||||
|
'AutoFormat.AutoParagraph' => false,
|
||||||
|
'AutoFormat.RemoveEmpty' => false,
|
||||||
|
],
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| HTMLPurifier definitions
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may specify a class that augments the HTML definitions used by
|
||||||
|
| HTMLPurifier. Additional HTML5 definitions are provided out of the box.
|
||||||
|
| When specifying a custom class, make sure it implements the interface:
|
||||||
|
|
|
||||||
|
| \Stevebauman\Purify\Definitions\Definition
|
||||||
|
|
|
||||||
|
| Note that these definitions are applied to every Purifier instance.
|
||||||
|
|
|
||||||
|
| Documentation: http://htmlpurifier.org/docs/enduser-customize.html
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'definitions' => Html5Definition::class,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| HTMLPurifier CSS definitions
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may specify a class that augments the CSS definitions used by
|
||||||
|
| HTMLPurifier. When specifying a custom class, make sure it implements
|
||||||
|
| the interface:
|
||||||
|
|
|
||||||
|
| \Stevebauman\Purify\Definitions\CssDefinition
|
||||||
|
|
|
||||||
|
| Note that these definitions are applied to every Purifier instance.
|
||||||
|
|
|
||||||
|
| CSS should be extending $definition->info['css-attribute'] = values
|
||||||
|
| See HTMLPurifier_CSSDefinition for further explanation
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'css-definitions' => null,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Serializer
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The storage implementation where HTMLPurifier can store its serializer files.
|
||||||
|
| If the filesystem cache is in use, the path must be writable through the
|
||||||
|
| storage disk by the web server, otherwise an exception will be thrown.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'serializer' => [
|
||||||
|
'driver' => env('CACHE_STORE', env('CACHE_DRIVER', 'file')),
|
||||||
|
'cache' => \Stevebauman\Purify\Cache\CacheDefinitionCache::class,
|
||||||
|
],
|
||||||
|
|
||||||
|
// 'serializer' => [
|
||||||
|
// 'disk' => env('FILESYSTEM_DISK', 'local'),
|
||||||
|
// 'path' => 'purify',
|
||||||
|
// 'cache' => \Stevebauman\Purify\Cache\FilesystemDefinitionCache::class,
|
||||||
|
// ],
|
||||||
|
|
||||||
|
];
|
@@ -1,20 +1,20 @@
|
|||||||
@extends('layouts.base')
|
@extends('layouts.base')
|
||||||
<div class="flex flex-col items-center justify-center h-full">
|
<div class="flex items-center justify-center min-h-screen">
|
||||||
<div>
|
<div class="w-full max-w-3xl px-8">
|
||||||
<p class="font-mono font-semibold text-red-500 text-7xl">500</p>
|
<p class="font-mono font-semibold text-red-500 text-[200px] leading-none">500</p>
|
||||||
<h1 class="mt-4 font-bold tracking-tight dark:text-white">Wait, this is not cool...</h1>
|
<h1 class="text-3xl font-bold tracking-tight dark:text-white">Wait, this is not cool...</h1>
|
||||||
<p class="text-base leading-7 text-neutral-300">There has been an error, we are working on it.
|
<p class="mt-2 text-lg leading-7 text-neutral-300">There has been an error with the following error message:</p>
|
||||||
</p>
|
|
||||||
@if ($exception->getMessage() !== '')
|
@if ($exception->getMessage() !== '')
|
||||||
<code class="mt-6 text-xs text-left text-red-500">Error: {{ $exception->getMessage() }}
|
<div class="mt-6 text-sm text-red-500">
|
||||||
</code>
|
{!! Purify::clean($exception->getMessage()) !!}
|
||||||
|
</div>
|
||||||
@endif
|
@endif
|
||||||
<div class="flex items-center mt-10 gap-x-6">
|
<div class="flex items-center mt-10 gap-6">
|
||||||
<a href="/">
|
<a href="/">
|
||||||
<x-forms.button>Go back home</x-forms.button>
|
<x-forms.button>Go back home</x-forms.button>
|
||||||
</a>
|
</a>
|
||||||
<a target="_blank" class="text-xs" href="{{ config('constants.urls.contact') }}">Contact
|
<a target="_blank" class="text-sm hover:text-neutral-300 flex items-center gap-1" href="{{ config('constants.urls.contact') }}">
|
||||||
support
|
Contact support
|
||||||
<x-external-link />
|
<x-external-link />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
</a>
|
</a>
|
||||||
@if (data_get($application, 'source.is_public') === false)
|
@if (data_get($application, 'source.is_public') === false)
|
||||||
<a target="_blank" class="hover:no-underline" href="{{ get_installation_path($application->source) }}">
|
<a target="_blank" class="hover:no-underline" href="{{ getInstallationPath($application->source) }}">
|
||||||
<x-forms.button>
|
<x-forms.button>
|
||||||
Open Git App
|
Open Git App
|
||||||
<x-external-link />
|
<x-external-link />
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
<livewire:source.github.create />
|
<livewire:source.github.create />
|
||||||
</x-modal-input>
|
</x-modal-input>
|
||||||
@if ($repositories->count() > 0)
|
@if ($repositories->count() > 0)
|
||||||
<a target="_blank" class="flex hover:no-underline" href="{{ get_installation_path($github_app) }}">
|
<a target="_blank" class="flex hover:no-underline" href="{{ getInstallationPath($github_app) }}">
|
||||||
<x-forms.button>
|
<x-forms.button>
|
||||||
Change Repositories on GitHub
|
Change Repositories on GitHub
|
||||||
<x-external-link />
|
<x-external-link />
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
@if (data_get($github_app, 'installation_id'))
|
@if (data_get($github_app, 'installation_id'))
|
||||||
<x-forms.button type="submit">Save</x-forms.button>
|
<x-forms.button type="submit">Save</x-forms.button>
|
||||||
<a href="{{ get_installation_path($github_app) }}">
|
<a href="{{ getInstallationPath($github_app) }}">
|
||||||
<x-forms.button>
|
<x-forms.button>
|
||||||
Update Repositories
|
Update Repositories
|
||||||
<x-external-link />
|
<x-external-link />
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
<span>You must complete this step before you can use this source!</span>
|
<span>You must complete this step before you can use this source!</span>
|
||||||
</div>
|
</div>
|
||||||
<a class="items-center justify-center box" href="{{ get_installation_path($github_app) }}">
|
<a class="items-center justify-center box" href="{{ getInstallationPath($github_app) }}">
|
||||||
Install Repositories on GitHub
|
Install Repositories on GitHub
|
||||||
</a>
|
</a>
|
||||||
@else
|
@else
|
||||||
@@ -106,7 +106,7 @@
|
|||||||
<div class="flex items-end gap-2 ">
|
<div class="flex items-end gap-2 ">
|
||||||
<h2 class="pt-4">Permissions</h2>
|
<h2 class="pt-4">Permissions</h2>
|
||||||
<x-forms.button wire:click.prevent="checkPermissions">Refetch</x-forms.button>
|
<x-forms.button wire:click.prevent="checkPermissions">Refetch</x-forms.button>
|
||||||
<a href="{{ get_permissions_path($github_app) }}">
|
<a href="{{ getPermissionsPath($github_app) }}">
|
||||||
<x-forms.button>
|
<x-forms.button>
|
||||||
Update
|
Update
|
||||||
<x-external-link />
|
<x-external-link />
|
||||||
|
Reference in New Issue
Block a user