Add all files

This commit is contained in:
root
2025-11-30 11:57:22 +01:00
parent 2ee51cac4f
commit 044271db3d
1476 changed files with 142544 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class CustomPropertySeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// Seed custom user properties
// for phone, company_name, country, address, address2, city, state, zip
DB::table('custom_properties')->insertOrIgnore([
[
'key' => 'phone',
'name' => 'Phone',
'model' => 'App\Models\User',
'type' => 'string',
'non_editable' => 0,
'required' => 1,
'show_on_invoice' => 0,
'validation' => 'string|max:255',
],
[
'key' => 'company_name',
'name' => 'Company Name',
'model' => 'App\Models\User',
'type' => 'string',
'non_editable' => 0,
'required' => 0,
'show_on_invoice' => 1,
'validation' => 'string|max:255',
],
[
'key' => 'address',
'name' => 'Address',
'model' => 'App\Models\User',
'type' => 'string',
'non_editable' => 0,
'required' => 1,
'show_on_invoice' => 1,
'validation' => 'string|max:255',
],
[
'key' => 'address2',
'name' => 'Address 2',
'model' => 'App\Models\User',
'type' => 'string',
'non_editable' => 0,
'required' => 0,
'show_on_invoice' => 0,
'validation' => 'string|max:255',
],
[
'key' => 'city',
'name' => 'City',
'model' => 'App\Models\User',
'type' => 'string',
'non_editable' => 0,
'required' => 1,
'show_on_invoice' => 1,
'validation' => 'string|max:255',
],
[
'key' => 'state',
'name' => 'State',
'model' => 'App\Models\User',
'type' => 'string',
'non_editable' => 0,
'required' => 1,
'show_on_invoice' => 1,
'validation' => 'string|max:255',
],
[
'key' => 'zip',
'name' => 'ZIP',
'model' => 'App\Models\User',
'type' => 'string',
'non_editable' => 0,
'required' => 1,
'show_on_invoice' => 1,
'validation' => 'string|max:255',
],
]);
DB::table('custom_properties')->insertOrIgnore([
[
'key' => 'country',
'name' => 'Country',
'model' => 'App\Models\User',
'type' => 'select',
'non_editable' => 0,
'required' => 1,
'show_on_invoice' => 1,
'allowed_values' => json_encode(array_values(array_slice(config('app.countries', []), 1))),
'validation' => 'string|max:255',
],
]);
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace Database\Seeders;
use App\Models\Role;
use App\Models\Setting;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*/
public function run(): void
{
// User::factory(10)->create();
Role::updateOrCreate(['name' => 'admin'], ['permissions' => ['*']]);
foreach (\App\Classes\Settings::settings() as $settings) {
foreach ($settings as $setting) {
if (!isset($setting['default'])) {
continue;
}
if (in_array($setting['name'], ['mail_header', 'mail_footer', 'mail_css'])) {
// Read from file in ./data/<name>
$setting['default'] = file_get_contents(__DIR__ . '/data/' . $setting['name']);
}
Setting::firstOrCreate([
'key' => $setting['name'],
], [
'value' => $setting['default'],
'type' => $setting['database_type'] ?? 'string',
]);
}
}
// Seed default currency (USD)
if (\App\Models\Currency::count() === 0) {
\App\Models\Currency::create([
'code' => 'USD',
'prefix' => '$',
'suffix' => '',
'format' => '1.000,00',
]);
}
\App\Providers\SettingsProvider::flushCache();
$this->call([
CustomPropertySeeder::class,
EmailTemplateSeeder::class,
]);
}
}

View File

@@ -0,0 +1,214 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class EmailTemplateSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
DB::table('email_templates')->insertOrIgnore([
[
'key' => 'new_login_detected',
'subject' => 'New login detected',
'body' => <<<'HTML'
# New login detected
A new login was detected on your account.
- IP: {{ $ip }}
- Device: {{ $device }}
- Time: {{ $time }}
**If this was you**
You can ignore this message, there is no need to take any action.
**If this wasn't you**
Please reset your password [here]({{ route('password.request') }}).
HTML,
],
[
'key' => 'new_invoice_created',
'subject' => 'New invoice created',
'body' => <<<'HTML'
# New invoice created
A new invoice was created on your account.
Total amount: **{{ $total }}**
<div class="table">
| Item | Quantity | Price |
| :------: | :------: | :------: |
@foreach ($items as $item)
| {{ $item->description }} | {{ $item->quantity }} | {{ $item->price }} |
@endforeach
</div>
<div class="action">
<a class="button button-blue" href="{{ route('invoices.show', $invoice) }}">
Go to invoice
</a>
</div>
@if($has_subscription)
You have a active subscription, the invoice will be automatically paid.
@endif
HTML,
],
[
'key' => 'new_order_created',
'subject' => 'New order created',
'body' => <<<'HTML'
# New order created
A new order was created on your account.
**Order details**
<div class="table">
| Item | Quantity | Price |
| :------: | :------: | :------: |
@foreach ($items as $item)
| {{ $item->product->name }} | {{ $item->quantity }} | {{ $item->formattedPrice }} |
@endforeach
</div>
HTML,
],
[
'key' => 'new_server_created',
'subject' => 'Server activated',
'body' => <<<'HTML'
# Server activated
Your server has been activated.
**Server details**
- Name: {{ $service->product->name }}
@isset($service->product->email_template)
**Server information**
{!! Str::markdown(Illuminate\View\Compilers\BladeCompiler::render($service->product->email_template, get_defined_vars()['__data'])) !!}
@endisset
HTML,
],
[
'key' => 'server_suspended',
'subject' => 'Server suspended',
'body' => <<<'HTML'
# Server suspended
Your server has been suspended due to a payment failure.
**Server details**
- Name: {{ $service->product->name }}
Please pay the invoice to reactivate the server.
HTML,
],
[
'key' => 'server_terminated',
'subject' => 'Server terminated',
'body' => <<<'HTML'
# Server terminated
Your server has been terminated.
**Server details**
- Name: {{ $service->product->name }}
Do you consider it a mistake?
<div class="action">
<a class="button button-blue" href="{{ route('tickets.create') }}">
Contact us
</a>
</div>
HTML,
],
[
'key' => 'new_ticket_message',
'subject' => 'New ticket reply',
'body' => <<<'HTML'
# New ticket reply
{{ $ticketMessage->user->name }} replied to your ticket.
**Message**
{!! Str::markdown($ticketMessage->message, [
'html_input' => 'strip',
'allow_unsafe_links' => false,
]) !!}
HTML,
],
[
'key' => 'email_verification',
'subject' => 'Email verification',
'body' => <<<'HTML'
# Email verification
Please verify your email address by clicking the link below.
<div class="action">
<a class="button button-blue" href="{{ $url }}">
Verify email
</a>
</div>
This link will expire in 60 minutes.
If you did not create an account, you can ignore this email.
HTML,
],
[
'key' => 'password_reset',
'subject' => 'Password reset',
'body' => <<<'HTML'
# Password reset
You are receiving this email because we received a password reset request for your account.
**Reset password**
<div class="action">
<a class="button button-blue" href="{{ $url }}">
Reset password
</a>
</div>
This password reset link will expire in 60 minutes.
If you did not request a password reset, no further action is required.
HTML,
],
[
'key' => 'service_cancellation_received',
'subject' => 'Service cancellation received',
'body' => <<<'HTML'
# Server Cancellation Received
We're sorry to see you go! Your server cancellation has been successfully received.
**Cancellation Details**
- Server: {{ $service->product->name }}
@if($cancellation->reason)
- Reason: {{ $cancellation->reason }}
@endif
- Requested at: {{ $cancellation->created_at->format('F j, Y, g:i A') }}
@if($cancellation->type === 'end_of_period')
Your server will remain active until {{ $service->expires_at->format('F j, Y') }} (end of your current billing period).
@else
Your server has been terminated immediately.
@endif
HTML,
],
]);
}
}

View File

@@ -0,0 +1,290 @@
/* Base */
body,
body *:not(html):not(style):not(br):not(tr):not(code) {
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
position: relative;
}
body {
-webkit-text-size-adjust: none;
background-color: #ffffff;
color: #718096;
height: 100%;
line-height: 1.4;
margin: 0;
padding: 0;
width: 100% !important;
}
p,
ul,
ol,
blockquote {
line-height: 1.4;
text-align: left;
}
a {
color: #3869d4;
}
a img {
border: none;
}
/* Typography */
h1 {
color: #3d4852;
font-size: 18px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
h2 {
font-size: 16px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
h3 {
font-size: 14px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
p {
font-size: 16px;
line-height: 1.5em;
margin-top: 0;
text-align: left;
}
p.sub {
font-size: 12px;
}
img {
max-width: 100%;
}
/* Layout */
.wrapper {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
background-color: #edf2f7;
margin: 0;
padding: 0;
width: 100%;
}
.content {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
margin: 0;
padding: 0;
width: 100%;
}
/* Header */
.header {
padding: 25px 0;
text-align: center;
}
.header a {
color: #3d4852;
font-size: 19px;
font-weight: bold;
text-decoration: none;
}
/* Logo */
.logo {
height: 75px;
max-height: 75px;
width: 75px;
}
/* Body */
.body {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
background-color: #edf2f7;
border-bottom: 1px solid #edf2f7;
border-top: 1px solid #edf2f7;
margin: 0;
padding: 0;
width: 100%;
}
.inner-body {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 570px;
background-color: #ffffff;
border-color: #e8e5ef;
border-radius: 2px;
border-width: 1px;
box-shadow: 0 2px 0 rgba(0, 0, 150, 0.025), 2px 4px 0 rgba(0, 0, 150, 0.015);
margin: 0 auto;
padding: 0;
width: 570px;
}
/* Subcopy */
.subcopy {
border-top: 1px solid #e8e5ef;
margin-top: 25px;
padding-top: 25px;
}
.subcopy p {
font-size: 14px;
}
/* Footer */
.footer {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 570px;
margin: 0 auto;
padding: 0;
text-align: center;
width: 570px;
}
.footer p {
color: #b0adc5;
font-size: 12px;
text-align: center;
}
.footer a {
color: #b0adc5;
text-decoration: underline;
}
/* Tables */
.table table {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
margin: 30px auto;
width: 100%;
}
.table th {
border-bottom: 1px solid #edeff2;
margin: 0;
padding-bottom: 8px;
}
.table td {
color: #74787e;
font-size: 15px;
line-height: 18px;
margin: 0;
padding: 10px 0;
}
.content-cell {
max-width: 100vw;
padding: 32px;
}
/* Buttons */
.action {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
margin: 30px auto;
padding: 0;
text-align: center;
width: 100%;
}
.button {
-webkit-text-size-adjust: none;
border-radius: 4px;
color: #fff;
display: inline-block;
overflow: hidden;
text-decoration: none;
}
.button-blue,
.button-primary {
background-color: #2d3748;
border-bottom: 8px solid #2d3748;
border-left: 18px solid #2d3748;
border-right: 18px solid #2d3748;
border-top: 8px solid #2d3748;
}
.button-green,
.button-success {
background-color: #48bb78;
border-bottom: 8px solid #48bb78;
border-left: 18px solid #48bb78;
border-right: 18px solid #48bb78;
border-top: 8px solid #48bb78;
}
.button-red,
.button-error {
background-color: #e53e3e;
border-bottom: 8px solid #e53e3e;
border-left: 18px solid #e53e3e;
border-right: 18px solid #e53e3e;
border-top: 8px solid #e53e3e;
}
/* Panels */
.panel {
border-left: #2d3748 solid 4px;
margin: 21px 0;
}
.panel-content {
background-color: #edf2f7;
color: #718096;
padding: 16px;
}
.panel-content p {
color: #718096;
}
.panel-item {
padding: 0;
}
.panel-item p:last-of-type {
margin-bottom: 0;
padding-bottom: 0;
}
/* Utilities */
.break-all {
word-break: break-all;
}

View File

@@ -0,0 +1,11 @@
<tr>
<td>
<table class="footer" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="content-cell" align="center">
© {{ date('Y') }} {{ config('app.name') }}. {{ __('All rights reserved.') }}
</td>
</tr>
</table>
</td>
</tr>

View File

@@ -0,0 +1,51 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>{{ config('app.name') }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="color-scheme" content="light">
<meta name="supported-color-schemes" content="light">
<style>
@media only screen and (max-width: 600px) {
.inner-body {
width: 100% !important;
}
.footer {
width: 100% !important;
}
}
@media only screen and (max-width: 500px) {
.button {
width: 100% !important;
}
}
{!! config('settings.mail_css') !!}
</style>
</head>
<body>
<table class="wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table class="content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
@if(config('settings.logo'))
<tr>
<td class="header">
<a href="{{ url('/') }}" style="display: inline-block;">
<img src="{{ url(Storage::url(config('settings.logo'))) }}" class="logo" alt="{{ config('app.name') }}">
</a>
</td>
</tr>
@endif
<!-- Email Body -->
<tr>
<td class="body" width="100%" cellpadding="0" cellspacing="0" style="border: hidden !important;">
<table class="inner-body" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
<!-- Body content -->
<tr>
<td class="content-cell">