Handle demo version of the app (#256)

This commit is contained in:
mchev
2025-01-12 13:56:52 +01:00
committed by GitHub
parent f52b73f517
commit 9bed81fe8f
7 changed files with 115 additions and 23 deletions

View File

@@ -6,6 +6,8 @@ use Illuminate\Console\Command;
use Illuminate\Console\ConfirmableTrait;
use Illuminate\Support\Facades\Artisan;
use function Laravel\Prompts\confirm;
class ResetApp extends Command
{
use ConfirmableTrait;
@@ -39,30 +41,49 @@ class ResetApp extends Command
*
* @return mixed
*/
/**
* Execute the console command to reset the application.
*
* This will:
* 1. Enable maintenance mode to prevent access during reset
* 2. Fresh migrate the database with initial seeds
* 3. Seed demo data using DemoSeeder
* 4. Clear all application caches
* 5. Disable maintenance mode
*
* The --force flag can be used to skip confirmation prompt.
*/
public function handle(): void
{
if (! $this->confirmToProceed()) {
return;
if (! $this->option('force')) {
if (! confirm('Are you sure you want to reset the application?')) {
$this->components->error('Reset cancelled');
return;
}
}
$this->info('Running migrate:fresh');
// Enable maintenance mode to prevent access during reset
$this->info('Activating maintenance mode...');
Artisan::call('down');
// Fresh migrate database and run initial seeds
$this->info('Running migrate:fresh');
Artisan::call('migrate:fresh --seed --force');
// Seed demo data
$this->info('Seeding database');
Artisan::call('db:seed', ['--class' => 'DemoSeeder', '--force' => true]);
$path = base_path('.env');
// Clear all application caches
$this->info('Clearing cache...');
Artisan::call('optimize:clear');
if (file_exists($path)) {
file_put_contents($path, str_replace(
'APP_DEBUG=true',
'APP_DEBUG=false',
file_get_contents($path)
));
}
// Disable maintenance mode
$this->info('Deactivating maintenance mode...');
Artisan::call('up');
$this->info('App has been reset successfully');
$this->info('App reset completed successfully!');
}
}

View File

@@ -60,6 +60,12 @@ class AppServiceProvider extends ServiceProvider
$this->bootAuth();
$this->bootBroadcast();
// In demo mode, prevent all outgoing emails and notifications
if (config('app.env') === 'demo') {
\Illuminate\Support\Facades\Mail::fake();
\Illuminate\Support\Facades\Notification::fake();
}
}
/**

View File

@@ -67,6 +67,7 @@ return Application::configure(basePath: dirname(__DIR__))
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Illuminate\Auth\Middleware\Authorize::class,
]);
})
->withExceptions(function (Exceptions $exceptions) {
//

View File

@@ -2,11 +2,15 @@
namespace Database\Seeders;
use App\Models\Address;
use App\Models\Company;
use App\Models\CompanySetting;
use App\Models\Customer;
use App\Models\Setting;
use App\Models\User;
use App\Space\InstallUtils;
use Illuminate\Database\Seeder;
use Silber\Bouncer\BouncerFacade;
use Vinkla\Hashids\Facades\Hashids;
class DemoSeeder extends Seeder
{
@@ -15,14 +19,60 @@ class DemoSeeder extends Seeder
*/
public function run(): void
{
$user = User::whereIs('super admin')->first();
// Create demo user
$user = User::factory()->create([
'email' => 'demo@invoiceshelf.com',
'name' => 'Demo User',
'role' => 'super admin',
'password' => 'demo',
]);
$user->setSettings(['language' => 'en']);
// Create demo company
$company = Company::factory()->create([
'name' => 'Demo Company',
'owner_id' => $user->id,
'slug' => 'demo-company',
]);
Address::create(['company_id' => $user->companies()->first()->id, 'country_id' => 1]);
$company->unique_hash = Hashids::connection(Company::class)->encode($company->id);
$company->save();
$company->setupDefaultData();
$user->companies()->attach($company->id);
BouncerFacade::scope()->to($company->id);
$user->assign('super admin');
// Set default user settings
$user->setSettings([
'language' => 'en',
'timezone' => 'UTC',
'date_format' => 'DD-MM-YYYY',
'currency_id' => 1, // USD
]);
// Set company settings
CompanySetting::setSettings([
'currency' => 1,
'date_format' => 'DD-MM-YYYY',
'language' => 'en',
'timezone' => 'UTC',
'fiscal_year' => 'calendar_year',
'tax_per_item' => false,
'discount_per_item' => false,
'invoice_prefix' => 'INV-',
'estimate_prefix' => 'EST-',
'payment_prefix' => 'PAY-',
], $company->id);
// Create demo customers
Customer::factory()->count(5)->create([
'company_id' => $company->id,
]);
// Mark profile setup as complete
Setting::setSetting('profile_complete', 'COMPLETED');
// Create installation marker
InstallUtils::createDbMarker();
}
}

View File

@@ -57,7 +57,7 @@
<script setup>
import axios from 'axios'
import { ref, computed } from 'vue'
import { ref, computed, onMounted } from 'vue'
import { useNotificationStore } from '@/scripts/stores/notification'
import { useRouter } from 'vue-router'
import { required, email, helpers } from '@vuelidate/validators'
@@ -120,4 +120,12 @@ async function onSubmit() {
isLoading.value = false
}
}
// Pre-fill demo credentials if in demo environment
onMounted(() => {
if (window.demo_mode) {
authStore.loginData.email = 'demo@invoiceshelf.com'
authStore.loginData.password = 'demo'
}
})
</script>

View File

@@ -64,6 +64,10 @@
@endif
@if(config('app.env') === 'demo')
window.demo_mode = true
@endif
window.InvoiceShelf.start()
</script>
</body>

View File

@@ -3,13 +3,15 @@
use App\Models\CompanySetting;
use App\Models\RecurringInvoice;
use App\Space\InstallUtils;
use Illuminate\Foundation\Inspiring;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Schedule;
Artisan::command('inspire', function () {
$this->comment(Inspiring::quote());
})->purpose('Display an inspiring quote')->hourly();
// Only run in demo environment
if (config('app.env') === 'demo') {
Schedule::command('reset:app --force')
->daily()
->runInBackground()
->withoutOverlapping();
}
if (InstallUtils::isDbCreated()) {
Schedule::command('check:invoices:status')