mirror of
https://github.com/crater-invoice/crater.git
synced 2025-10-27 11:41:09 -04:00
Add Domain Verification on Onboarding Wizard
This commit is contained in:
@ -3,8 +3,12 @@
|
||||
namespace Crater\Http\Controllers\V1\Mobile;
|
||||
|
||||
use Crater\Http\Controllers\Controller;
|
||||
use Crater\Http\Requests\DomainEnvironmentRequest;
|
||||
use Crater\Models\User;
|
||||
use Crater\Space\EnvironmentManager;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
@ -40,4 +44,9 @@ class AuthController extends Controller
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
return Auth::check();
|
||||
}
|
||||
}
|
||||
|
||||
33
app/Http/Controllers/V1/Onboarding/AppDomainController.php
Normal file
33
app/Http/Controllers/V1/Onboarding/AppDomainController.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Crater\Http\Controllers\V1\Onboarding;
|
||||
|
||||
use Crater\Http\Controllers\Controller;
|
||||
use Crater\Http\Requests\DomainEnvironmentRequest;
|
||||
use Crater\Space\EnvironmentManager;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
|
||||
class AppDomainController extends Controller
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @param DomainEnvironmentRequest $request
|
||||
*/
|
||||
public function __invoke(DomainEnvironmentRequest $request)
|
||||
{
|
||||
Artisan::call('optimize:clear');
|
||||
|
||||
$environmentManager = new EnvironmentManager();
|
||||
|
||||
$results = $environmentManager->saveDomainVariables($request);
|
||||
|
||||
if (in_array('error', $results)) {
|
||||
return response()->json($results);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -32,16 +32,20 @@ class DatabaseConfigurationController extends Controller
|
||||
Artisan::call('config:clear');
|
||||
Artisan::call('cache:clear');
|
||||
|
||||
$status = pcntl_fork();
|
||||
pcntl_wait($status);
|
||||
|
||||
$results = $this->environmentManager->saveDatabaseVariables($request);
|
||||
|
||||
if (array_key_exists("success", $results)) {
|
||||
Artisan::call('key:generate --force');
|
||||
Artisan::call('optimize:clear');
|
||||
Artisan::call('config:clear');
|
||||
Artisan::call('cache:clear');
|
||||
Artisan::call('storage:link');
|
||||
Artisan::call('migrate --seed --force');
|
||||
}
|
||||
|
||||
|
||||
return response()->json($results);
|
||||
}
|
||||
|
||||
|
||||
@ -19,7 +19,6 @@ class FinishController extends Controller
|
||||
{
|
||||
\Storage::disk('local')->put('database_created', 'database_created');
|
||||
|
||||
$user = User::where('role', 'super admin')->first();
|
||||
Auth::login($user);
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
}
|
||||
|
||||
25
app/Http/Controllers/V1/Onboarding/LoginController.php
Normal file
25
app/Http/Controllers/V1/Onboarding/LoginController.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Crater\Http\Controllers\V1\Onboarding;
|
||||
|
||||
use Auth;
|
||||
use Crater\Http\Controllers\Controller;
|
||||
use Crater\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class LoginController extends Controller
|
||||
{
|
||||
/**
|
||||
* Handle the incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
$user = User::where('role', 'super admin')->first();
|
||||
Auth::login($user);
|
||||
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
}
|
||||
@ -30,9 +30,6 @@ class DatabaseEnvironmentRequest extends FormRequest
|
||||
'required',
|
||||
'url',
|
||||
],
|
||||
'app_domain' => [
|
||||
'required',
|
||||
],
|
||||
'database_connection' => [
|
||||
'required',
|
||||
'string',
|
||||
@ -50,9 +47,6 @@ class DatabaseEnvironmentRequest extends FormRequest
|
||||
'required',
|
||||
'url',
|
||||
],
|
||||
'app_domain' => [
|
||||
'required',
|
||||
],
|
||||
'database_connection' => [
|
||||
'required',
|
||||
'string',
|
||||
|
||||
32
app/Http/Requests/DomainEnvironmentRequest.php
Normal file
32
app/Http/Requests/DomainEnvironmentRequest.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Crater\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class DomainEnvironmentRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'app_domain' => [
|
||||
'required',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -262,6 +262,11 @@ class User extends Authenticatable implements HasMedia
|
||||
$query->orWhere('users.id', $customer_id);
|
||||
}
|
||||
|
||||
public function scopeWhereSuperAdmin($query)
|
||||
{
|
||||
$query->orWhere('role', 'super admin');
|
||||
}
|
||||
|
||||
public function scopeApplyInvoiceFilters($query, array $filters)
|
||||
{
|
||||
$filters = collect($filters);
|
||||
|
||||
@ -4,6 +4,7 @@ namespace Crater\Space;
|
||||
|
||||
use Crater\Http\Requests\DatabaseEnvironmentRequest;
|
||||
use Crater\Http\Requests\DiskEnvironmentRequest;
|
||||
use Crater\Http\Requests\DomainEnvironmentRequest;
|
||||
use Crater\Http\Requests\MailEnvironmentRequest;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
@ -486,4 +487,35 @@ class EnvironmentManager
|
||||
'old_default_driver' => $oldDefaultDriver,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save sanctum statful domain to the .env file.
|
||||
*
|
||||
* @param DomainEnvironmentRequest $request
|
||||
* @return array
|
||||
*/
|
||||
public function saveDomainVariables(DomainEnvironmentRequest $request)
|
||||
{
|
||||
try {
|
||||
file_put_contents($this->envPath, str_replace(
|
||||
'SANCTUM_STATEFUL_DOMAINS='.env('SANCTUM_STATEFUL_DOMAINS'),
|
||||
'SANCTUM_STATEFUL_DOMAINS='.$request->app_domain,
|
||||
file_get_contents($this->envPath)
|
||||
));
|
||||
|
||||
file_put_contents($this->envPath, str_replace(
|
||||
'SESSION_DOMAIN='.config('session.domain'),
|
||||
'SESSION_DOMAIN='.explode(':', $request->app_domain)[0],
|
||||
file_get_contents($this->envPath)
|
||||
));
|
||||
} catch (Exception $e) {
|
||||
return [
|
||||
'error' => 'domain_verification_failed'
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => 'domain_variable_save_successfully'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
2
resources/assets/js/bootstrap.js
vendored
2
resources/assets/js/bootstrap.js
vendored
@ -126,9 +126,11 @@ global.axios.interceptors.response.use(undefined, function (err) {
|
||||
} else {
|
||||
if (
|
||||
err.response.data &&
|
||||
err.config.url !== '/api/v1/auth/check' &&
|
||||
(err.response.statusText === 'Unauthorized' ||
|
||||
err.response.data === ' Unauthorized.')
|
||||
) {
|
||||
console.log(err.response)
|
||||
// Unauthorized and log out
|
||||
store.dispatch('notification/showNotification', {
|
||||
type: 'error',
|
||||
|
||||
@ -19,7 +19,7 @@ export default {
|
||||
stepHeadingContainer: 'heading-section',
|
||||
stepTitle: 'text-2xl not-italic font-semibold leading-7 text-black',
|
||||
stepDescription:
|
||||
'w-full mt-2.5 mb-8 text-sm not-italic leading-snug text-gray-500 lg:w-7/12 md:w-7/12 sm:w-7/12',
|
||||
'w-full mt-2.5 mb-8 text-sm not-italic text-gray-600 lg:w-7/12 md:w-7/12 sm:w-7/12',
|
||||
},
|
||||
variants: {},
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import i18n from '../plugins/i18n';
|
||||
import i18n from '../plugins/i18n'
|
||||
|
||||
export default {
|
||||
addClass(el, className) {
|
||||
@ -142,12 +142,20 @@ export default {
|
||||
},
|
||||
|
||||
checkValidDomainUrl(url) {
|
||||
if (url.includes('localhost')) {
|
||||
if (url.includes('localhost') || url.includes('127.0.0.1')) {
|
||||
return true
|
||||
}
|
||||
|
||||
let pattern = new RegExp(
|
||||
'^([0-9A-Za-z-\\.@:%_+~#=]+)+((\\.[a-zA-Z]{2,3})+)(/(.)*)?(\\?(.)*)?'
|
||||
)
|
||||
'^(https?:\\/\\/)?' + // protocol
|
||||
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
|
||||
'((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
|
||||
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
|
||||
'(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
|
||||
'(\\#[-a-z\\d_]*)?$',
|
||||
'i'
|
||||
) // fragment locator
|
||||
|
||||
return !!pattern.test(url)
|
||||
},
|
||||
|
||||
|
||||
@ -1071,6 +1071,14 @@
|
||||
"permission_confirm_desc": "Folder permission check failed",
|
||||
"permission_desc": "Below is the list of folder permissions which are required in order for the app to work. If the permission check fails, make sure to update your folder permissions."
|
||||
},
|
||||
"verify_domain": {
|
||||
"title": "Domain Verification",
|
||||
"desc": "Crater uses Session based authentication which requires domain verification for security purposes. Please enter the domain on which you will be accessing your web application.",
|
||||
"app_domain": "App Domain",
|
||||
"verify_now": "Verify Now",
|
||||
"success": "Domain Verify Successfully.",
|
||||
"verify_and_continue": "Verify And Continue"
|
||||
},
|
||||
"mail": {
|
||||
"host": "Mail Host",
|
||||
"port": "Mail Port",
|
||||
@ -1097,6 +1105,7 @@
|
||||
},
|
||||
"errors": {
|
||||
"migrate_failed": "Migrate Failed",
|
||||
"domain_verification_failed": "Domain verification failed",
|
||||
"database_variables_save_error": "Cannot write configuration to .env file. Please check its file permissions",
|
||||
"mail_variables_save_error": "Email configuration failed.",
|
||||
"connection_failed": "Database connection failed",
|
||||
@ -1104,6 +1113,7 @@
|
||||
},
|
||||
"success": {
|
||||
"mail_variables_save_successfully": "Email configured successfully",
|
||||
"domain_variable_save_successfully": "Domain configured successfully",
|
||||
"database_variables_save_successfully": "Database configured successfully."
|
||||
}
|
||||
},
|
||||
|
||||
@ -21,7 +21,6 @@ import SiteSidebar from './partials/TheSiteSidebar.vue'
|
||||
import BaseModal from '../../components/base/modal/BaseModal'
|
||||
import { RefreshIcon } from '@vue-hero-icons/solid'
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import BaseNotification from '../../components/base/BaseNotification.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -30,7 +29,6 @@ export default {
|
||||
SiteFooter,
|
||||
BaseModal,
|
||||
RefreshIcon,
|
||||
BaseNotification,
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div class="site-wrapper h-full text-base">
|
||||
<div class="h-full text-base site-wrapper">
|
||||
<base-notification />
|
||||
<div class="container mx-auto">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
import SystemRequirement from './WizardSystemRequirementStep'
|
||||
import Permission from './WizardPermissionStep'
|
||||
import Database from './WizardDatabaseStep'
|
||||
import VerifyDomain from './WizardVerifyDomainStep'
|
||||
import EmailConfiguration from './WizardEmailConfigStep'
|
||||
import UserProfile from './WizardUserProfileStep'
|
||||
import CompanyInfo from './WizardCompanyInfoStep'
|
||||
@ -32,10 +33,11 @@ export default {
|
||||
step_1: SystemRequirement,
|
||||
step_2: Permission,
|
||||
step_3: Database,
|
||||
step_4: EmailConfiguration,
|
||||
step_5: UserProfile,
|
||||
step_6: CompanyInfo,
|
||||
step_7: Settings,
|
||||
step_4: VerifyDomain,
|
||||
step_5: EmailConfiguration,
|
||||
step_6: UserProfile,
|
||||
step_7: CompanyInfo,
|
||||
step_8: Settings,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -69,16 +71,27 @@ export default {
|
||||
let status = {
|
||||
profile_complete: data,
|
||||
}
|
||||
|
||||
let response = await axios.post('/api/v1/onboarding/wizard-step', status)
|
||||
try {
|
||||
let response = await axios.post(
|
||||
'/api/v1/onboarding/wizard-step',
|
||||
status
|
||||
)
|
||||
return true
|
||||
} catch (e) {
|
||||
if (e?.response?.data?.message === 'The MAC is invalid.') {
|
||||
window.location.reload()
|
||||
}
|
||||
return false
|
||||
}
|
||||
},
|
||||
async setTab(data) {
|
||||
if (data) {
|
||||
this.setProfileComplete(data)
|
||||
let res = await this.setProfileComplete(data)
|
||||
if (!res) return false
|
||||
}
|
||||
this.step++
|
||||
|
||||
if (this.step <= 7) {
|
||||
if (this.step <= 8) {
|
||||
this.tab = 'step_' + this.step
|
||||
} else {
|
||||
// window.location.reload()
|
||||
|
||||
@ -297,7 +297,7 @@ export default {
|
||||
})
|
||||
}
|
||||
|
||||
this.$emit('next', 6)
|
||||
this.$emit('next', 7)
|
||||
this.isLoading = false
|
||||
}
|
||||
},
|
||||
|
||||
@ -16,7 +16,6 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { validationMixin } from 'vuelidate'
|
||||
import Mysql from './database/MysqlDatabase'
|
||||
import Pgsql from './database/PgsqlDatabase'
|
||||
import Sqlite from './database/SqliteDatabase'
|
||||
@ -67,15 +66,11 @@ export default {
|
||||
async next(databaseData) {
|
||||
this.isLoading = this.isFetching = true
|
||||
try {
|
||||
await window.axios.get('/sanctum/csrf-cookie')
|
||||
|
||||
let response = await window.axios.post(
|
||||
'/api/v1/onboarding/database/config',
|
||||
databaseData
|
||||
)
|
||||
|
||||
await window.axios.get('/sanctum/csrf-cookie')
|
||||
|
||||
if (response.data.success) {
|
||||
await window.axios.post('/api/v1/onboarding/finish')
|
||||
|
||||
|
||||
@ -67,7 +67,7 @@ export default {
|
||||
mailConfigData
|
||||
)
|
||||
if (response.data.success) {
|
||||
this.$emit('next', 4)
|
||||
this.$emit('next', 5)
|
||||
this.showNotification({
|
||||
type: 'success',
|
||||
message: this.$t('wizard.success.' + response.data.success),
|
||||
|
||||
@ -246,7 +246,7 @@ export default {
|
||||
|
||||
this.uploadAvatar(avatarData)
|
||||
}
|
||||
this.$emit('next', 5)
|
||||
this.$emit('next', 6)
|
||||
this.isLoading = false
|
||||
}
|
||||
return true
|
||||
|
||||
136
resources/assets/js/views/wizard/WizardVerifyDomainStep.vue
Normal file
136
resources/assets/js/views/wizard/WizardVerifyDomainStep.vue
Normal file
@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<sw-wizard-step
|
||||
:title="$t('wizard.verify_domain.title')"
|
||||
:description="$t('wizard.verify_domain.desc')"
|
||||
>
|
||||
<div class="w-full md:w-2/3">
|
||||
<sw-input-group
|
||||
:label="$t('wizard.verify_domain.app_domain')"
|
||||
:error="domainError"
|
||||
required
|
||||
>
|
||||
<sw-input
|
||||
:invalid="$v.formData.app_domain.$error"
|
||||
v-model.trim="formData.app_domain"
|
||||
type="text"
|
||||
name="name"
|
||||
placeholder="crater.com"
|
||||
@input="$v.formData.app_domain.$touch()"
|
||||
/>
|
||||
</sw-input-group>
|
||||
</div>
|
||||
|
||||
<p class="mt-4 mb-0 text-sm text-gray-600">Notes:</p>
|
||||
<ul class="w-full text-gray-600 list-disc list-inside">
|
||||
<li class="text-sm leading-8">
|
||||
App domain should not contain
|
||||
<b class="inline-block px-1 bg-gray-100 rounded-sm">https://</b> or
|
||||
<b class="inline-block px-1 bg-gray-100 rounded-sm">http</b> in front of
|
||||
the domain.
|
||||
</li>
|
||||
<li class="text-sm leading-8">
|
||||
If you're accessing the website on a different port, please mention the
|
||||
port. For example:
|
||||
<b class="inline-block px-1 bg-gray-100">localhost:8080</b>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<sw-button
|
||||
:loading="isLoading"
|
||||
:disabled="isLoading"
|
||||
class="mt-8"
|
||||
variant="primary"
|
||||
@click="verifyDomain"
|
||||
>
|
||||
{{ $t('wizard.verify_domain.verify_now') }}
|
||||
</sw-button>
|
||||
</sw-wizard-step>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ArrowRightIcon } from '@vue-hero-icons/solid'
|
||||
const { required } = require('vuelidate/lib/validators')
|
||||
import { mapActions } from 'vuex'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ArrowRightIcon,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formData: {
|
||||
app_domain: window.location.origin.replace(/(^\w+:|^)\/\//, ''),
|
||||
},
|
||||
isLoading: false,
|
||||
isShow: true,
|
||||
}
|
||||
},
|
||||
|
||||
validations: {
|
||||
formData: {
|
||||
app_domain: {
|
||||
required,
|
||||
isUrl(val) {
|
||||
return this.$utils.checkValidDomainUrl(val)
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
hasNext() {
|
||||
return false
|
||||
},
|
||||
domainError() {
|
||||
if (!this.$v.formData.app_domain.$error) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (!this.$v.formData.app_domain.required) {
|
||||
return this.$tc('validation.required')
|
||||
}
|
||||
|
||||
if (!this.$v.formData.app_domain.isUrl) {
|
||||
return this.$tc('validation.invalid_domain_url')
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
...mapActions('notification', ['showNotification']),
|
||||
listToggle() {
|
||||
this.isShow = !this.isShow
|
||||
},
|
||||
|
||||
async verifyDomain() {
|
||||
this.$v.formData.$touch()
|
||||
|
||||
if (this.$v.formData.$invalid) {
|
||||
return true
|
||||
}
|
||||
|
||||
this.isLoading = true
|
||||
try {
|
||||
await window.axios.put('api/v1/onboarding/set-domain', this.formData)
|
||||
|
||||
await window.axios.get('/sanctum/csrf-cookie')
|
||||
|
||||
await window.axios.post('/api/v1/onboarding/login')
|
||||
|
||||
let driverRes = await window.axios.get('/api/v1/auth/check')
|
||||
|
||||
if (driverRes.data) {
|
||||
await this.$emit('next', 4)
|
||||
}
|
||||
this.isLoading = false
|
||||
} catch (e) {
|
||||
this.showNotification({
|
||||
type: 'error',
|
||||
message: this.$t('wizard.errors.domain_verification_failed'),
|
||||
})
|
||||
this.isLoading = false
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<form action="" @submit.prevent="next()">
|
||||
<div>
|
||||
<div class="grid grid-cols-1 gap-4 mb-4 md:grid-cols-2 lg:mb-6 md:mb-6">
|
||||
<div class="grid grid-cols-1 gap-5 md:grid-cols-2 lg:mb-6 md:mb-6">
|
||||
<sw-input-group
|
||||
:label="$t('wizard.database.app_url')"
|
||||
:error="urlError"
|
||||
@ -16,23 +16,6 @@
|
||||
/>
|
||||
</sw-input-group>
|
||||
|
||||
<sw-input-group
|
||||
:label="$t('wizard.database.app_domain')"
|
||||
:error="domainError"
|
||||
required
|
||||
>
|
||||
<sw-input
|
||||
:invalid="$v.databaseData.app_domain.$error"
|
||||
v-model.trim="databaseData.app_domain"
|
||||
type="text"
|
||||
name="name"
|
||||
placeholder="crater.com"
|
||||
@input="$v.databaseData.app_domain.$touch()"
|
||||
/>
|
||||
</sw-input-group>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 mb-4 md:grid-cols-2 lg:mb-6 md:mb-6">
|
||||
<sw-input-group
|
||||
:label="$t('wizard.database.connection')"
|
||||
:error="connectionError"
|
||||
@ -62,9 +45,7 @@
|
||||
@input="$v.databaseData.database_port.$touch()"
|
||||
/>
|
||||
</sw-input-group>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 mb-4 md:grid-cols-2 lg:mb-6 md:mb-6">
|
||||
<sw-input-group
|
||||
:label="$t('wizard.database.db_name')"
|
||||
:error="nameError"
|
||||
@ -92,9 +73,7 @@
|
||||
@input="$v.databaseData.database_username.$touch()"
|
||||
/>
|
||||
</sw-input-group>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 mb-6 md:grid-cols-2">
|
||||
<sw-input-group :label="$t('wizard.database.password')">
|
||||
<sw-input
|
||||
v-model.trim="databaseData.database_password"
|
||||
@ -102,7 +81,9 @@
|
||||
name="name"
|
||||
/>
|
||||
</sw-input-group>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 mb-6 md:grid-cols-2">
|
||||
<sw-input-group
|
||||
:label="$t('wizard.database.host')"
|
||||
:error="hostnameError"
|
||||
@ -168,7 +149,6 @@ export default {
|
||||
database_username: null,
|
||||
database_password: null,
|
||||
app_url: window.location.origin,
|
||||
app_domain: window.location.origin.replace(/(^\w+:|^)\/\//, ''),
|
||||
},
|
||||
connections: ['sqlite', 'mysql', 'pgsql', 'sqlsrv'],
|
||||
}
|
||||
@ -197,12 +177,6 @@ export default {
|
||||
return this.$utils.checkValidUrl(val)
|
||||
},
|
||||
},
|
||||
app_domain: {
|
||||
required,
|
||||
isUrl(val) {
|
||||
return this.$utils.checkValidDomainUrl(val)
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
@ -219,19 +193,6 @@ export default {
|
||||
return this.$tc('validation.invalid_url')
|
||||
}
|
||||
},
|
||||
domainError() {
|
||||
if (!this.$v.databaseData.app_domain.$error) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (!this.$v.databaseData.app_domain.required) {
|
||||
return this.$tc('validation.required')
|
||||
}
|
||||
|
||||
if (!this.$v.databaseData.app_domain.isUrl) {
|
||||
return this.$tc('validation.invalid_domain_url')
|
||||
}
|
||||
},
|
||||
connectionError() {
|
||||
if (!this.$v.databaseData.database_connection.$error) {
|
||||
return ''
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<form action="" @submit.prevent="next()">
|
||||
<div>
|
||||
<div class="grid grid-cols-1 gap-4 mb-4 md:grid-cols-2 lg:mb-6 md:mb-6">
|
||||
<div class="grid grid-cols-1 gap-5 md:grid-cols-2 lg:mb-6 md:mb-6">
|
||||
<sw-input-group
|
||||
:label="$t('wizard.database.app_url')"
|
||||
:error="urlError"
|
||||
@ -16,23 +16,6 @@
|
||||
/>
|
||||
</sw-input-group>
|
||||
|
||||
<sw-input-group
|
||||
:label="$t('wizard.database.app_domain')"
|
||||
:error="domainError"
|
||||
required
|
||||
>
|
||||
<sw-input
|
||||
:invalid="$v.databaseData.app_domain.$error"
|
||||
v-model.trim="databaseData.app_domain"
|
||||
type="text"
|
||||
name="name"
|
||||
placeholder="crater.com"
|
||||
@input="$v.databaseData.app_domain.$touch()"
|
||||
/>
|
||||
</sw-input-group>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 mb-4 md:grid-cols-2 lg:mb-6 md:mb-6">
|
||||
<sw-input-group
|
||||
:label="$t('wizard.database.connection')"
|
||||
:error="connectionError"
|
||||
@ -62,9 +45,7 @@
|
||||
@input="$v.databaseData.database_port.$touch()"
|
||||
/>
|
||||
</sw-input-group>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 mb-4 md:grid-cols-2 lg:mb-6 md:mb-6">
|
||||
<sw-input-group
|
||||
:label="$t('wizard.database.db_name')"
|
||||
:error="nameError"
|
||||
@ -92,9 +73,7 @@
|
||||
@input="$v.databaseData.database_username.$touch()"
|
||||
/>
|
||||
</sw-input-group>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 mb-6 md:grid-cols-2">
|
||||
<sw-input-group :label="$t('wizard.database.password')">
|
||||
<sw-input
|
||||
v-model.trim="databaseData.database_password"
|
||||
@ -134,8 +113,7 @@
|
||||
|
||||
<script>
|
||||
import { SaveIcon } from '@vue-hero-icons/outline'
|
||||
import { validationMixin } from 'vuelidate'
|
||||
const { required, numeric, url } = require('vuelidate/lib/validators')
|
||||
const { required, numeric } = require('vuelidate/lib/validators')
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -168,7 +146,6 @@ export default {
|
||||
database_username: null,
|
||||
database_password: null,
|
||||
app_url: window.location.origin,
|
||||
app_domain: window.location.origin.replace(/(^\w+:|^)\/\//, ''),
|
||||
},
|
||||
connections: ['sqlite', 'mysql', 'pgsql', 'sqlsrv'],
|
||||
}
|
||||
@ -187,19 +164,6 @@ export default {
|
||||
return this.$tc('validation.invalid_url')
|
||||
}
|
||||
},
|
||||
domainError() {
|
||||
if (!this.$v.databaseData.app_domain.$error) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (!this.$v.databaseData.app_domain.required) {
|
||||
return this.$tc('validation.required')
|
||||
}
|
||||
|
||||
if (!this.$v.databaseData.app_domain.isUrl) {
|
||||
return this.$tc('validation.invalid_domain_url')
|
||||
}
|
||||
},
|
||||
connectionError() {
|
||||
if (!this.$v.databaseData.database_connection.$error) {
|
||||
return ''
|
||||
@ -274,12 +238,6 @@ export default {
|
||||
return this.$utils.checkValidUrl(val)
|
||||
},
|
||||
},
|
||||
app_domain: {
|
||||
required,
|
||||
isUrl(val) {
|
||||
return this.$utils.checkValidDomainUrl(val)
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<form action="" @submit.prevent="next()">
|
||||
<div>
|
||||
<div class="grid grid-cols-1 gap-4 mb-4 md:grid-cols-2 lg:mb-6 md:mb-6">
|
||||
<div class="grid grid-cols-1 gap-5 md:grid-cols-2 lg:mb-6 md:mb-6">
|
||||
<sw-input-group
|
||||
:label="$t('wizard.database.app_url')"
|
||||
:error="urlError"
|
||||
@ -16,23 +16,6 @@
|
||||
/>
|
||||
</sw-input-group>
|
||||
|
||||
<sw-input-group
|
||||
:label="$t('wizard.database.app_domain')"
|
||||
:error="domainError"
|
||||
required
|
||||
>
|
||||
<sw-input
|
||||
:invalid="$v.databaseData.app_domain.$error"
|
||||
v-model.trim="databaseData.app_domain"
|
||||
type="text"
|
||||
name="name"
|
||||
placeholder="crater.com"
|
||||
@input="$v.databaseData.app_domain.$touch()"
|
||||
/>
|
||||
</sw-input-group>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 mb-4 md:grid-cols-2 lg:mb-6 md:mb-6">
|
||||
<sw-input-group
|
||||
:label="$t('wizard.database.connection')"
|
||||
:error="connectionError"
|
||||
@ -81,8 +64,7 @@
|
||||
|
||||
<script>
|
||||
import { SaveIcon } from '@vue-hero-icons/outline'
|
||||
import { validationMixin } from 'vuelidate'
|
||||
const { required, numeric, url } = require('vuelidate/lib/validators')
|
||||
const { required } = require('vuelidate/lib/validators')
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -111,7 +93,6 @@ export default {
|
||||
database_connection: 'mysql',
|
||||
database_name: null,
|
||||
app_url: window.location.origin,
|
||||
app_domain: window.location.origin.replace(/(^\w+:|^)\/\//, ''),
|
||||
},
|
||||
connections: ['sqlite', 'mysql', 'pgsql', 'sqlsrv'],
|
||||
}
|
||||
@ -130,12 +111,6 @@ export default {
|
||||
return this.$utils.checkValidUrl(val)
|
||||
},
|
||||
},
|
||||
app_domain: {
|
||||
required,
|
||||
isUrl(val) {
|
||||
return this.$utils.checkValidDomainUrl(val)
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
@ -152,19 +127,6 @@ export default {
|
||||
return this.$tc('validation.invalid_url')
|
||||
}
|
||||
},
|
||||
domainError() {
|
||||
if (!this.$v.databaseData.app_domain.$error) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (!this.$v.databaseData.app_domain.required) {
|
||||
return this.$tc('validation.required')
|
||||
}
|
||||
|
||||
if (!this.$v.databaseData.app_domain.isUrl) {
|
||||
return this.$tc('validation.invalid_domain_url')
|
||||
}
|
||||
},
|
||||
connectionError() {
|
||||
if (!this.$v.databaseData.database_connection.$error) {
|
||||
return ''
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<form action="" @submit.prevent="next()">
|
||||
<div>
|
||||
<div class="grid grid-cols-1 gap-4 mb-4 md:grid-cols-2 lg:mb-6 md:mb-6">
|
||||
<div class="grid grid-cols-1 gap-5 md:grid-cols-2 lg:mb-6 md:mb-6">
|
||||
<sw-input-group
|
||||
:label="$t('wizard.database.app_url')"
|
||||
:error="urlError"
|
||||
@ -16,23 +16,6 @@
|
||||
/>
|
||||
</sw-input-group>
|
||||
|
||||
<sw-input-group
|
||||
:label="$t('wizard.database.app_domain')"
|
||||
:error="domainError"
|
||||
required
|
||||
>
|
||||
<sw-input
|
||||
:invalid="$v.databaseData.app_domain.$error"
|
||||
v-model.trim="databaseData.app_domain"
|
||||
type="text"
|
||||
name="name"
|
||||
placeholder="crater.com"
|
||||
@input="$v.databaseData.app_domain.$touch()"
|
||||
/>
|
||||
</sw-input-group>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 mb-4 md:grid-cols-2 lg:mb-6 md:mb-6">
|
||||
<sw-input-group
|
||||
:label="$t('wizard.database.connection')"
|
||||
:error="connectionError"
|
||||
@ -62,9 +45,7 @@
|
||||
@input="$v.databaseData.database_port.$touch()"
|
||||
/>
|
||||
</sw-input-group>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 mb-4 md:grid-cols-2 lg:mb-6 md:mb-6">
|
||||
<sw-input-group
|
||||
:label="$t('wizard.database.db_name')"
|
||||
:error="nameError"
|
||||
@ -92,9 +73,7 @@
|
||||
@input="$v.databaseData.database_username.$touch()"
|
||||
/>
|
||||
</sw-input-group>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 mb-6 md:grid-cols-2">
|
||||
<sw-input-group :label="$t('wizard.database.password')">
|
||||
<sw-input
|
||||
v-model.trim="databaseData.database_password"
|
||||
@ -134,8 +113,7 @@
|
||||
|
||||
<script>
|
||||
import { SaveIcon } from '@vue-hero-icons/outline'
|
||||
import { validationMixin } from 'vuelidate'
|
||||
const { required, numeric, url } = require('vuelidate/lib/validators')
|
||||
const { required, numeric } = require('vuelidate/lib/validators')
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -168,7 +146,6 @@ export default {
|
||||
database_username: null,
|
||||
database_password: null,
|
||||
app_url: window.location.origin,
|
||||
app_domain: window.location.origin.replace(/(^\w+:|^)\/\//, ''),
|
||||
},
|
||||
connections: ['sqlite', 'mysql', 'pgsql', 'sqlsrv'],
|
||||
}
|
||||
@ -197,12 +174,6 @@ export default {
|
||||
return this.$utils.checkValidUrl(val)
|
||||
},
|
||||
},
|
||||
app_domain: {
|
||||
required,
|
||||
isUrl(val) {
|
||||
return this.$utils.checkValidDomainUrl(val)
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
@ -219,19 +190,6 @@ export default {
|
||||
return this.$tc('validation.invalid_url')
|
||||
}
|
||||
},
|
||||
domainError() {
|
||||
if (!this.$v.databaseData.app_domain.$error) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (!this.$v.databaseData.app_domain.required) {
|
||||
return this.$tc('validation.required')
|
||||
}
|
||||
|
||||
if (!this.$v.databaseData.app_domain.isUrl) {
|
||||
return this.$tc('validation.invalid_domain_url')
|
||||
}
|
||||
},
|
||||
connectionError() {
|
||||
if (!this.$v.databaseData.database_connection.$error) {
|
||||
return ''
|
||||
|
||||
@ -36,8 +36,10 @@ use Crater\Http\Controllers\V1\Invoice\SendInvoiceController;
|
||||
use Crater\Http\Controllers\V1\Item\ItemsController;
|
||||
use Crater\Http\Controllers\V1\Item\UnitsController;
|
||||
use Crater\Http\Controllers\V1\Mobile\AuthController;
|
||||
use Crater\Http\Controllers\V1\Onboarding\AppDomainController;
|
||||
use Crater\Http\Controllers\V1\Onboarding\DatabaseConfigurationController;
|
||||
use Crater\Http\Controllers\V1\Onboarding\FinishController;
|
||||
use Crater\Http\Controllers\V1\Onboarding\LoginController;
|
||||
use Crater\Http\Controllers\V1\Onboarding\OnboardingWizardController;
|
||||
use Crater\Http\Controllers\V1\Onboarding\PermissionsController;
|
||||
use Crater\Http\Controllers\V1\Onboarding\RequirementsController;
|
||||
@ -133,12 +135,17 @@ Route::prefix('/v1')->group(function () {
|
||||
|
||||
Route::get('/onboarding/database/config', [DatabaseConfigurationController::class, 'getDatabaseEnvironment']);
|
||||
|
||||
Route::put('/onboarding/set-domain', AppDomainController::class);
|
||||
|
||||
Route::post('/onboarding/login', LoginController::class);
|
||||
|
||||
Route::post('/onboarding/finish', FinishController::class);
|
||||
});
|
||||
|
||||
|
||||
Route::middleware(['auth:sanctum', 'admin'])->group(function () {
|
||||
|
||||
|
||||
// Bootstrap
|
||||
//----------------------------------
|
||||
|
||||
@ -151,6 +158,12 @@ Route::prefix('/v1')->group(function () {
|
||||
Route::get('/dashboard', DashboardController::class);
|
||||
|
||||
|
||||
// Auth check
|
||||
//----------------------------------
|
||||
|
||||
Route::get('/auth/check', [AuthController::class, 'check']);
|
||||
|
||||
|
||||
// Search users
|
||||
//----------------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user