Skip to content
This repository was archived by the owner on Aug 8, 2025. It is now read-only.

Commit 9f8bae6

Browse files
committed
tokens
1 parent 3ace589 commit 9f8bae6

File tree

9 files changed

+196
-57
lines changed

9 files changed

+196
-57
lines changed

app/Actions/APITokens.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
namespace App\Actions;
4+
5+
use Illuminate\Contracts\Filesystem\FileNotFoundException;
6+
use Illuminate\Support\Facades\File;
7+
8+
class APITokens
9+
{
10+
/**
11+
* @throws FileNotFoundException
12+
*/
13+
public function setup(string $path, string $frontend, bool $enabled): void
14+
{
15+
if ($enabled) {
16+
return;
17+
}
18+
19+
File::delete($path.'/app/Http/Controllers/TokenController.php');
20+
File::delete($path.'/app/Http/Resources/TokenResource.php');
21+
File::delete($path.'/tests/Feature/TokenTest.php');
22+
23+
$settingsRoutes = File::get($path.'/routes/settings.php');
24+
$settingsRoutes = remove_blocks($settingsRoutes, 'tokens-routes');
25+
File::put($path.'/routes/settings.php', $settingsRoutes);
26+
27+
File::deleteDirectory($path.'/resources/js/pages/tokens');
28+
29+
$tokensMenuPattern = '/\{\s*title:\s*\'API Tokens\',\s*href:\s*route\([^)]+\),\s*icon:\s*CommandIcon,\s*\},?\n?/m';
30+
31+
$settingsLayout = match ($frontend) {
32+
'Vue' => File::get($path.'/resources/js/layouts/settings/layout.vue'),
33+
'React' => File::get($path.'/resources/js/layouts/settings/layout.tsx'),
34+
default => throw new \RuntimeException("Unsupported frontend: {$frontend}"),
35+
};
36+
37+
$settingsLayout = preg_replace($tokensMenuPattern, '', $settingsLayout);
38+
File::put($path.'/resources/js/layouts/settings/layout.tsx', $settingsLayout);
39+
40+
$userModel = File::get($path.'/app/Models/User.php');
41+
$userModel = str_replace('use HasApiTokens;', '', $userModel);
42+
$userModel = str_replace('* @property Collection<int, PersonalAccessToken> $tokens', '', $userModel);
43+
File::put($path.'/app/Models/User.php', $userModel);
44+
45+
File::delete($path.'/database/migrations/2025_07_10_214649_create_personal_access_tokens_table.php');
46+
File::delete($path.'/app/Models/PersonalAccessToken.php');
47+
}
48+
49+
public function cleanup(string $path, bool $enabled): void
50+
{
51+
if ($enabled) {
52+
return;
53+
}
54+
55+
exec('composer remove laravel/sanctum --working-dir='.$path);
56+
}
57+
}

app/Actions/Application.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
namespace App\Actions;
4+
5+
use Illuminate\Support\Facades\File;
6+
use RuntimeException;
7+
8+
class Application
9+
{
10+
public function setup(string $path, string $directory): void
11+
{
12+
$this->clone($directory);
13+
14+
if (! File::exists($path.'/.env')) {
15+
File::copy($path.'/.env.example', $path.'/.env');
16+
}
17+
18+
exec("composer install --working-dir={$path}");
19+
20+
exec("cd {$path} && php artisan key:generate");
21+
}
22+
23+
public function clone(string $directory): void
24+
{
25+
if (is_dir($directory)) {
26+
throw new RuntimeException("Directory {$directory} already exists. Please choose a different name or remove the existing directory.");
27+
}
28+
29+
$repositoryUrl = '[email protected]:php-saas/php-saas.git';
30+
31+
exec("git clone {$repositoryUrl} {$directory}");
32+
}
33+
34+
public function cleanup(string $path): void
35+
{
36+
File::deleteDirectory($path.'/.github');
37+
File::deleteDirectory($path.'/.git');
38+
File::delete($path.'/.gitignore');
39+
File::move($path.'/.gitignore.final', $path.'/.gitignore');
40+
}
41+
}

app/Actions/Billing.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace App\Actions;
4+
5+
class Billing
6+
{
7+
public function setup(string $path, bool $enabled): void
8+
{
9+
if ($enabled) {
10+
return;
11+
}
12+
13+
$billingRoutes = file_get_contents($path.'/routes/billing.php');
14+
$billingRoutes = remove_blocks($billingRoutes, 'billing-routes');
15+
file_put_contents($path.'/routes/billing.php', $billingRoutes);
16+
17+
$billingLayout = file_get_contents($path.'/resources/js/layouts/billing/layout.vue');
18+
$billingLayout = preg_replace('/\{\s*title:\s*\'Billing\',\s*href:\s*route\([^)]+\),\s*icon:\s*CreditCardIcon,\s*\},?\n?/m', '', $billingLayout);
19+
file_put_contents($path.'/resources/js/layouts/billing/layout.vue', $billingLayout);
20+
}
21+
22+
public function cleanup(string $path, bool $enabled): void
23+
{
24+
if ($enabled) {
25+
return;
26+
}
27+
28+
exec('composer remove laravel/cashier --working-dir='.$path);
29+
exec('composer remove laravel/cashier-paddle --working-dir='.$path);
30+
}
31+
}

app/Actions/Frontend.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ public function setup(string $path, string $stack): void
1818
];
1919

2020
foreach ($renames as $from => $to) {
21-
$from = $path . '/' . sprintf($from, strtolower($stack));
22-
$to = $path . '/' . sprintf($to, strtolower($stack));
21+
$from = $path.'/'.sprintf($from, strtolower($stack));
22+
$to = $path.'/'.sprintf($to, strtolower($stack));
2323

24-
if (!File::exists($from)) {
24+
if (! File::exists($from)) {
2525
throw new RuntimeException("File {$from} does not exist. Please check the template.");
2626
}
2727

@@ -31,7 +31,7 @@ public function setup(string $path, string $stack): void
3131

3232
public function cleanup(string $path): void
3333
{
34-
File::deleteDirectory($path . '/resources/js-vue');
35-
File::deleteDirectory($path . '/resources/js-react');
34+
File::deleteDirectory($path.'/resources/js-vue');
35+
File::deleteDirectory($path.'/resources/js-react');
3636
}
3737
}

app/Actions/Git.php

Lines changed: 0 additions & 28 deletions
This file was deleted.

app/Actions/Tests.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ public function setup(string $path, string $stack): void
1414
];
1515

1616
foreach ($renames as $from => $to) {
17-
$from = $path . '/' . sprintf($from, strtolower($stack));
18-
$to = $path . '/' . sprintf($to, strtolower($stack));
17+
$from = $path.'/'.sprintf($from, strtolower($stack));
18+
$to = $path.'/'.sprintf($to, strtolower($stack));
1919

20-
if (!File::exists($from)) {
20+
if (! File::exists($from)) {
2121
throw new RuntimeException("File {$from} does not exist. Please check the template.");
2222
}
2323

@@ -27,15 +27,15 @@ public function setup(string $path, string $stack): void
2727

2828
public function cleanup(string $path, string $stack): void
2929
{
30-
File::deleteDirectory($path . '/tests-pest');
31-
File::deleteDirectory($path . '/tests-phpunit');
30+
File::deleteDirectory($path.'/tests-pest');
31+
File::deleteDirectory($path.'/tests-phpunit');
3232

3333
switch ($stack) {
3434
case 'Pest':
35-
exec('composer remove phpunit/phpunit --no-interaction --working-dir=' . $path);
35+
exec('composer remove phpunit/phpunit --no-interaction --working-dir='.$path);
3636
break;
3737
case 'PHPUnit':
38-
exec('composer remove pestphp/pest --no-interaction --working-dir=' . $path);
38+
exec('composer remove pestphp/pest --no-interaction --working-dir='.$path);
3939
break;
4040
default:
4141
}

app/Commands/NewCommand.php

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22

33
namespace App\Commands;
44

5-
use App\Actions\Composer;
5+
use App\Actions\APITokens;
6+
use App\Actions\Application;
67
use App\Actions\Frontend;
7-
use App\Actions\Git;
88
use App\Actions\Tests;
99
use Illuminate\Console\Command;
10+
use Illuminate\Contracts\Filesystem\FileNotFoundException;
1011
use Illuminate\Support\Facades\File;
12+
1113
use function Laravel\Prompts\select;
14+
use function Termwind\render;
1215

1316
class NewCommand extends Command
1417
{
@@ -28,64 +31,86 @@ class NewCommand extends Command
2831

2932
protected string $apiTokens = '';
3033

34+
/**
35+
* @throws FileNotFoundException
36+
*/
3137
public function handle(): void
3238
{
39+
render(<<<'HTML'
40+
▗▄▄▖ ▗▖ ▗▖▗▄▄▖ ▗▄▄▖ ▗▄▖ ▗▄▖ ▗▄▄▖
41+
▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌ ▐▌ ▐▌ ▐▌▐▌ ▐▌▐▌
42+
▐▛▀▘ ▐▛▀▜▌▐▛▀▘ ▝▀▚▖▐▛▀▜▌▐▛▀▜▌ ▝▀▚▖
43+
▐▌ ▐▌ ▐▌▐▌ ▗▄▄▞▘▐▌ ▐▌▐▌ ▐▌▗▄▄▞▘
44+
HTML
45+
);
46+
3347
$name = $this->argument('name');
3448

35-
$this->path = getcwd() . '/' . $name;
49+
$this->path = getcwd().'/'.$name;
3650

3751
$this->collectInputs();
3852

39-
app(Git::class)->clone($name);
40-
app(Frontend::class)->setup($this->path, $this->frontend);
41-
app(Tests::class)->setup($this->path, $this->tests);
42-
app(Composer::class)->setup($this->path);
53+
$this->setup();
4354

4455
$this->cleanup();
4556

4657
$this->info("Application '{$name}' created successfully.");
4758
}
4859

60+
/**
61+
* @throws FileNotFoundException
62+
*/
63+
private function setup(): void
64+
{
65+
app(Application::class)->setup($this->path, $this->argument('name'));
66+
app(Frontend::class)->setup($this->path, $this->frontend);
67+
app(Tests::class)->setup($this->path, $this->tests);
68+
app(APITokens::class)->setup($this->path, $this->frontend, $this->apiTokens === 'Yes');
69+
}
70+
4971
private function collectInputs(): void
5072
{
5173
$this->frontend = 'React';
52-
$this->tests = 'Pest';
74+
$this->tests = 'PHPUnit';
5375
$this->projects = 'Projects';
5476
$this->billing = 'Cashier Paddle';
55-
$this->apiTokens = 'Yes';
77+
$this->apiTokens = 'No';
5678

5779
return;
58-
$this->frontend = select("Which frontend stack would you like to use?", [
80+
$this->frontend = select('Which frontend stack would you like to use?', [
5981
'React',
6082
'Vue',
6183
]);
62-
$this->tests = select("Which testing framework would you like to use?", [
84+
$this->tests = select('Which testing framework would you like to use?', [
6385
'PHPUnit',
6486
'Pest',
6587
]);
66-
$this->projects = select("Do you want Projects, Organizations or Teams?", [
88+
$this->projects = select('Do you want Projects, Organizations or Teams?', [
6789
'Projects',
6890
'Organizations',
6991
'Teams',
7092
'None',
7193
]);
72-
$this->billing = select("Which payment provider do you want for Billing?", [
94+
$this->billing = select('Which payment provider do you want for Billing?', [
7395
'Cashier Paddle',
7496
'Cashier Stripe',
7597
'None',
7698
]);
77-
$this->apiTokens = select("Do you want to include API tokens?", [
99+
$this->apiTokens = select('Do you want to include API tokens?', [
78100
'Yes',
79101
'No',
80102
]);
81103
}
82104

83105
private function cleanup(): void
84106
{
85-
app(Git::class)->cleanup($this->path);
107+
app(Application::class)->cleanup($this->path);
86108
app(Frontend::class)->cleanup($this->path);
87109
app(Tests::class)->cleanup($this->path, $this->tests);
110+
app(APITokens::class)->cleanup($this->path, $this->apiTokens === 'Yes');
111+
112+
File::delete($this->path.'/use.sh');
88113

89-
File::delete($this->path . '/use.sh');
114+
exec($this->path.'/vendor/bin/pint --parallel');
90115
}
91116
}

app/Support/helpers.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
function remove_blocks(string $content, string $block): string
4+
{
5+
return preg_replace(
6+
'/\/\/ php-saas: '.$block.'.*?\/\/ php-saas: end-'.$block.'/s',
7+
'',
8+
$content
9+
);
10+
}

composer.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@
2929
"App\\": "app/",
3030
"Database\\Factories\\": "database/factories/",
3131
"Database\\Seeders\\": "database/seeders/"
32-
}
32+
},
33+
"files": [
34+
"app/Support/helpers.php"
35+
]
3336
},
3437
"autoload-dev": {
3538
"psr-4": {

0 commit comments

Comments
 (0)