vue components refactoring

This commit is contained in:
Čarodej
2022-04-13 16:19:10 +02:00
parent 6a4bfa8bfe
commit 338f8664b7
251 changed files with 1068 additions and 1943 deletions

View File

@@ -0,0 +1,126 @@
<template>
<PageTab>
<!--Adsense basic setup-->
<div v-if="adsense" class="card shadow-card">
<FormLabel>
{{ $t('Basic Setup') }}
</FormLabel>
<AppInputSwitch :title="$t('Allow Google Adsense')" :description="$t('Allow ads on app pages.')">
<SwitchInput
@input="$updateText('/admin/settings', 'allowed_adsense', adsense.allowedService)"
v-model="adsense.allowedService"
class="switch"
:state="adsense.allowedService"
/>
</AppInputSwitch>
<AppInputText
:title="$t('Client Id')"
:description="$t('Paste your Adsense Client ID e.g. ca-pub-XXXXXXXXXXXXXXXXX')"
:is-last="true"
>
<input
@input="$updateText('/admin/settings', 'adsense_client_id', adsense.clientId, true)"
v-model="adsense.clientId"
:placeholder="$t('Client Id...')"
type="text"
class="focus-border-theme input-dark"
/>
</AppInputText>
</div>
<!--Adsense places-->
<div v-if="adsense" class="card shadow-card">
<FormLabel>
{{ $t('Ads') }}
</FormLabel>
<AppInputText
:title="$t('File Viewport Banner')"
:description="$t('This banner will be showed above user files')"
>
<textarea
rows="3"
@input="$updateText('/admin/settings', 'adsense_banner_01', adsense.banner01, true)"
v-model="adsense.banner01"
:placeholder="$t('Paste the <ins></ins> tag here...')"
type="text"
class="focus-border-theme input-dark"
/>
</AppInputText>
<AppInputText
:title="$t('Download Page Banner')"
:description="$t('This banner will be showed below file download page')"
>
<textarea
rows="3"
@input="$updateText('/admin/settings', 'adsense_banner_02', adsense.banner02, true)"
v-model="adsense.banner02"
:placeholder="$t('Paste the <ins></ins> tag here...')"
type="text"
class="focus-border-theme input-dark"
/>
</AppInputText>
<AppInputText
:title="$t('Homepage Banner')"
:description="$t('This banner will be showed on the homepage')"
:is-last="true"
>
<textarea
rows="3"
@input="$updateText('/admin/settings', 'adsense_banner_03', adsense.banner03, true)"
v-model="adsense.banner03"
:placeholder="$t('Paste the <ins></ins> tag here...')"
type="text"
class="focus-border-theme input-dark"
/>
</AppInputText>
</div>
</PageTab>
</template>
<script>
import SwitchInput from '../../../../components/Inputs/SwitchInput'
import AppInputButton from '../../../../components/Forms/Layouts/AppInputButton'
import AppInputSwitch from '../../../../components/Forms/Layouts/AppInputSwitch'
import FormLabel from '../../../../components/UI/Labels/FormLabel'
import AppInputText from '../../../../components/Forms/Layouts/AppInputText'
import PageTab from '../../../../components/Layout/PageTab'
import { mapGetters } from 'vuex'
export default {
name: 'Adsense',
components: {
AppInputButton,
AppInputSwitch,
AppInputText,
SwitchInput,
FormLabel,
PageTab,
},
computed: {
...mapGetters(['config']),
},
data() {
return {
adsense: {
allowedService: undefined,
clientId: undefined,
banner01: undefined,
},
}
},
created() {
this.adsense = {
allowedService: this.config.allowedAdsense,
clientId: this.config.adsenseClientId,
banner01: this.config.adsenseBanner01,
banner02: this.config.adsenseBanner02,
banner03: this.config.adsenseBanner03,
}
},
}
</script>

View File

@@ -0,0 +1,163 @@
<template>
<PageTab :is-loading="isLoading">
<div v-if="app" class="card shadow-card">
<FormLabel>
{{ $t('admin_settings.appearance.section_general') }}
</FormLabel>
<AppInputSwitch :title="$t('color_theme')" :description="$t('color_theme_description')">
<input
@input="$updateText('/admin/settings', 'app_color', app.color)"
v-model="app.color"
:placeholder="$t('admin_settings.appearance.title_plac')"
type="color"
/>
</AppInputSwitch>
<AppInputText :title="$t('admin_settings.appearance.title')">
<input
@input="$updateText('/admin/settings', 'app_title', app.title)"
v-model="app.title"
:placeholder="$t('admin_settings.appearance.title_plac')"
type="text"
class="focus-border-theme input-dark"
/>
</AppInputText>
<AppInputText :title="$t('admin_settings.appearance.description')" :is-last="true">
<input
@input="$updateText('/admin/settings', 'app_description', app.description)"
v-model="app.description"
:placeholder="$t('admin_settings.appearance.description_plac')"
type="text"
class="focus-border-theme input-dark"
/>
</AppInputText>
</div>
<div v-if="app" class="card shadow-card">
<FormLabel>
{{ $t('branding') }}
</FormLabel>
<AppInputText :title="$t('admin_settings.appearance.logo')">
<ImageInput
@input="$updateImage('/admin/settings', 'app_logo', app.logo)"
:image="$getImage(app.logo)"
v-model="app.logo"
/>
</AppInputText>
<AppInputText :title="$t('app_logo_dark_mode')">
<ImageInput
@input="$updateImage('/admin/settings', 'app_logo_dark', app.logo_dark)"
:image="$getImage(app.logo_dark)"
v-model="app.logo_dark"
/>
</AppInputText>
<AppInputText :title="$t('admin_settings.appearance.logo_horizontal')">
<ImageInput
@input="$updateImage('/admin/settings', 'app_logo_horizontal', app.logo_horizontal)"
:image="$getImage(app.logo_horizontal)"
v-model="app.logo_horizontal"
/>
</AppInputText>
<AppInputText :title="$t('app_logo_horizontal_dark_mode')">
<ImageInput
@input="$updateImage('/admin/settings', 'app_logo_horizontal_dark', app.logo_horizontal_dark)"
:image="$getImage(app.logo_horizontal_dark)"
v-model="app.logo_horizontal_dark"
/>
</AppInputText>
<AppInputText :title="$t('admin_settings.appearance.favicon')">
<ImageInput
@input="$updateImage('/admin/settings', 'app_favicon', app.favicon)"
:image="$getImage(app.favicon)"
v-model="app.favicon"
/>
</AppInputText>
<AppInputText :title="$t('og_image')" :description="$t('og_image_description')">
<ImageInput
@input="$updateImage('/admin/settings', 'app_og_image', app.og_image)"
:image="$getImage(app.og_image)"
v-model="app.og_image"
/>
</AppInputText>
<AppInputText :title="$t('app_touch_icon')" :description="$t('app_touch_icon_description')" :is-last="true">
<ImageInput
@input="$updateImage('/admin/settings', 'app_touch_icon', app.touch_icon)"
:image="$getImage(app.touch_icon)"
v-model="app.touch_icon"
/>
</AppInputText>
</div>
</PageTab>
</template>
<script>
import AppInputSwitch from '../../../../components/Forms/Layouts/AppInputSwitch'
import AppInputText from '../../../../components/Forms/Layouts/AppInputText'
import { ValidationProvider, ValidationObserver } from 'vee-validate/dist/vee-validate.full'
import PageTabGroup from '../../../../components/Layout/PageTabGroup'
import SelectInput from '../../../../components/Inputs/SelectInput'
import ImageInput from '../../../../components/Inputs/ImageInput'
import FormLabel from '../../../../components/UI/Labels/FormLabel'
import ButtonBase from '../../../../components/UI/Buttons/ButtonBase'
import PageTab from '../../../../components/Layout/PageTab'
import InfoBox from '../../../../components/UI/Others/InfoBox'
import { required } from 'vee-validate/dist/rules'
import axios from 'axios'
export default {
name: 'AppAppearance',
components: {
AppInputSwitch,
AppInputText,
ValidationObserver,
ValidationProvider,
PageTabGroup,
SelectInput,
ImageInput,
ButtonBase,
FormLabel,
required,
PageTab,
InfoBox,
},
data() {
return {
isLoading: true,
app: undefined,
}
},
mounted() {
axios
.get('/api/admin/settings', {
params: {
column: 'app_title|app_description|app_logo|app_logo_dark|app_favicon|app_logo_horizontal|app_logo_horizontal_dark|app_color|app_og_image|app_touch_icon',
},
})
.then((response) => {
this.app = {
logo_horizontal: response.data.app_logo_horizontal,
logo_horizontal_dark: response.data.app_logo_horizontal_dark,
description: response.data.app_description,
favicon: response.data.app_favicon,
title: response.data.app_title,
color: response.data.app_color,
logo: response.data.app_logo,
logo_dark: response.data.app_logo_dark,
og_image: response.data.app_og_image,
touch_icon: response.data.app_touch_icon,
}
})
.finally(() => {
this.isLoading = false
})
},
}
</script>

View File

@@ -0,0 +1,370 @@
<template>
<PageTab :is-loading="isLoading">
<!--Broadcasting setup-->
<ValidationObserver
@submit.prevent="broadcastSetupSubmit"
ref="broadcastSetup"
v-slot="{ invalid }"
tag="form"
class="card shadow-card"
>
<FormLabel icon="wifi">
{{ $t('broadcasting') }}
</FormLabel>
<ValidationProvider tag="div" mode="passive" name="Broadcast Driver" rules="required" v-slot="{ errors }">
<AppInputText title="Broadcast Driver" :error="errors[0]">
<SelectInput
v-model="broadcast.driver"
:options="broadcastDrivers"
placeholder="Select your broadcast driver"
:isError="errors[0]"
/>
</AppInputText>
</ValidationProvider>
<div v-if="broadcast.driver === 'native'">
<ValidationProvider tag="div" mode="passive" name="Host" rules="required" v-slot="{ errors }">
<AppInputText title="Hostname or IP" :error="errors[0]">
<input
class="focus-border-theme input-dark"
v-model="broadcast.host"
placeholder="Type your hostname or IP"
type="text"
:class="{ '!border-rose-600': errors[0] }"
/>
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Port" rules="required" v-slot="{ errors }">
<AppInputText title="Port" :error="errors[0]">
<input
class="focus-border-theme input-dark"
v-model="broadcast.port"
placeholder="Type your port"
type="text"
:class="{ '!border-rose-600': errors[0] }"
/>
</AppInputText>
</ValidationProvider>
</div>
<div v-if="broadcast.driver === 'pusher'">
<ValidationProvider tag="div" mode="passive" name="App ID" rules="required" v-slot="{ errors }">
<AppInputText title="App ID" :error="errors[0]">
<input
class="focus-border-theme input-dark"
v-model="broadcast.id"
placeholder="Type your app id"
type="text"
:class="{ '!border-rose-600': errors[0] }"
/>
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Key" rules="required" v-slot="{ errors }">
<AppInputText title="Key" :error="errors[0]">
<input
class="focus-border-theme input-dark"
v-model="broadcast.key"
placeholder="Paste your key"
type="text"
:class="{ '!border-rose-600': errors[0] }"
/>
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Secret" rules="required" v-slot="{ errors }">
<AppInputText title="Secret" :error="errors[0]">
<input
class="focus-border-theme input-dark"
v-model="broadcast.secret"
placeholder="Paste your secret"
type="text"
:class="{ '!border-rose-600': errors[0] }"
/>
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Cluster" rules="required" v-slot="{ errors }">
<AppInputText title="Cluster" :error="errors[0]">
<SelectInput
v-model="broadcast.cluster"
:options="pusherClusters"
placeholder="Select your cluster"
:isError="errors[0]"
/>
</AppInputText>
</ValidationProvider>
</div>
<ButtonBase
:loading="isSendingBroadcastForm"
:disabled="isSendingBroadcastForm"
type="submit"
button-style="theme"
class="w-full sm:w-auto"
>
{{ $t('save_broadcast_settings') }}
</ButtonBase>
</ValidationObserver>
<!--Storage setup-->
<ValidationObserver
@submit.prevent="storageSetupSubmit"
ref="storageSetup"
v-slot="{ invalid }"
tag="form"
class="card shadow-card"
>
<StorageSetup v-model="storage" />
<ButtonBase
:loading="isSendingStorageForm"
:disabled="isSendingStorageForm"
type="submit"
button-style="theme"
class="mt-6 w-full sm:mt-7 sm:w-auto"
>
{{ $t('save_storage_settings') }}
</ButtonBase>
</ValidationObserver>
<!--Mail setup-->
<ValidationObserver
@submit.prevent="emailSetupSubmit"
ref="EmailSetup"
v-slot="{ invalid }"
tag="form"
class="card shadow-card"
>
<MailSetup v-model="mail" />
<ButtonBase
:loading="isSendingEmailForm"
:disabled="isSendingEmailForm"
type="submit"
button-style="theme"
class="mt-6 w-full sm:mt-7 sm:w-auto"
>
{{ $t('admin_settings.email.save_button') }}
</ButtonBase>
</ValidationObserver>
</PageTab>
</template>
<script>
import { ValidationObserver, ValidationProvider } from 'vee-validate/dist/vee-validate.full'
import SelectInput from '../../../../components/Inputs/SelectInput'
import FormLabel from '../../../../components/UI/Labels/FormLabel'
import AppInputText from '../../../../components/Forms/Layouts/AppInputText'
import StorageSetup from '../../../../components/Forms/StorageSetup'
import ButtonBase from '../../../../components/UI/Buttons/ButtonBase'
import PageTab from '../../../../components/Layout/PageTab'
import MailSetup from '../../../../components/Forms/MailSetup'
import { events } from '../../../../bus'
import axios from 'axios'
export default {
name: 'AppEnvironment',
components: {
ValidationObserver,
ValidationProvider,
StorageSetup,
AppInputText,
SelectInput,
ButtonBase,
MailSetup,
FormLabel,
PageTab,
},
data() {
return {
storage: undefined,
mail: undefined,
isLoading: false,
isSendingEmailForm: false,
isSendingStorageForm: false,
isSendingBroadcastForm: false,
broadcast: {
driver: undefined,
id: undefined,
key: undefined,
secret: undefined,
cluster: undefined,
port: undefined,
host: undefined,
},
broadcastDrivers: [
{
label: 'Pusher',
value: 'pusher',
},
{
label: 'VueFileManager Broadcast Server',
value: 'native',
},
{
label: 'None',
value: 'none',
},
],
pusherClusters: [
{
label: 'US East (N. Virginia)',
value: 'mt1',
},
{
label: 'Asia Pacific (Singapore)',
value: 'ap1',
},
{
label: 'Asia Pacific (Mumbai)',
value: 'ap2',
},
{
label: 'US East (Ohio)',
value: 'us2',
},
{
label: 'Asia Pacific (Tokyo)',
value: 'ap3',
},
{
label: 'US West (Oregon)',
value: 'us3',
},
{
label: 'Asia Pacific (Sydney)',
value: 'ap4',
},
{
label: 'EU (Ireland)',
value: 'eu',
},
{
label: 'South America (São Paulo)',
value: 'sa1',
},
],
}
},
methods: {
async broadcastSetupSubmit() {
// Validate fields
const isValid = await this.$refs.broadcastSetup.validate()
if (!isValid) return
// Start loading
this.isSendingBroadcastForm = true
axios
.post('/api/admin/settings/broadcast', { ...this.broadcast })
.then(() => {
events.$emit('toaster', {
type: 'success',
message: this.$t('broadcast_driver_updated'),
})
})
.catch(() => {
events.$emit('toaster', {
type: 'danger',
message: this.$t('popup_error.title'),
})
})
.finally(() => {
this.isSendingBroadcastForm = false
this.broadcast = {
driver: undefined,
id: undefined,
key: undefined,
secret: undefined,
cluster: undefined,
host: undefined,
port: undefined,
}
})
},
async storageSetupSubmit() {
// Validate fields
const isValid = await this.$refs.storageSetup.validate()
if (!isValid) return
// Start loading
this.isSendingStorageForm = true
axios
.post('/api/admin/settings/storage', {
storage: this.storage,
})
.then(() => {
this.storage = undefined
events.$emit('toaster', {
type: 'success',
message: this.$t('storage_driver_updated'),
})
})
.catch((error) => {
if ([401, 500].includes(error.response.status)) {
events.$emit('alert:open', {
title: error.response.data.title || this.$t('popup_error.title'),
message: error.response.data.message,
})
} else {
events.$emit('toaster', {
type: 'danger',
message: this.$t('popup_error.title'),
})
}
})
.finally(() => {
this.isSendingStorageForm = false
})
},
async emailSetupSubmit() {
// Validate fields
const isValid = await this.$refs.EmailSetup.validate()
if (!isValid) return
// Start loading
this.isSendingEmailForm = true
axios
.post('/api/admin/settings/email', {
mailDriver: this.mail.driver,
postmark: this.mail.postmark,
mailgun: this.mail.mailgun,
smtp: this.mail.smtp,
ses: this.mail.ses,
})
.then(() => {
events.$emit('toaster', {
type: 'success',
message: this.$t('toaster.email_set'),
})
})
.catch((error) => {
if (error.response.status === 401 && error.response.data.type === 'mailer-connection-error') {
events.$emit('alert:open', {
title: 'Mailer Connection Error - Wrong Credentials',
message: error.response.data.message,
})
} else {
events.$emit('toaster', {
type: 'danger',
message: this.$t('popup_error.title'),
})
}
})
.finally(() => {
this.isSendingEmailForm = false
})
},
},
}
</script>

View File

@@ -0,0 +1,588 @@
<template>
<PageTab :is-loading="isLoading">
<PageTabGroup v-if="app">
<div class="form block-form">
<div class="card shadow-card">
<FormLabel>
{{ $t('homepage') }}
</FormLabel>
<AppInputSwitch
:title="$t('allow_homepage')"
:description="$t('allow_homepage_note')"
:is-last="true"
>
<SwitchInput
@input="$updateText('/admin/settings', 'allow_homepage', app.allow_homepage)"
v-model="app.allow_homepage"
class="switch"
:state="app.allow_homepage"
/>
</AppInputSwitch>
</div>
<!--Header-->
<div class="card shadow-card">
<FormLabel>Header Title</FormLabel>
<div class="block-wrapper">
<img src="/assets/images/admin/main-header.jpg" alt="Main Header" class="page-image" />
</div>
<div class="block-wrapper">
<label>Title:</label>
<ValidationProvider
tag="div"
mode="passive"
name="App Title"
rules="required"
v-slot="{ errors }"
>
<input
@input="$updateText('/admin/settings', 'header_title', app.header_title)"
v-model="app.header_title"
type="text"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>Description:</label>
<ValidationProvider
tag="div"
mode="passive"
name="App Description"
rules="required"
v-slot="{ errors }"
>
<textarea
@input="$updateText('/admin/settings', 'header_description', app.header_description)"
rows="2"
v-model="app.header_description"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
></textarea>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
</div>
<!--Features title-->
<div class="card shadow-card">
<FormLabel>Features Title</FormLabel>
<div class="block-wrapper">
<div>
<div class="inline-wrapper">
<div class="switch-label">
<label class="input-label"> Show section: </label>
</div>
<SwitchInput
@input="$updateText('/admin/settings', 'section_features', app.section_features)"
v-model="app.section_features"
class="switch"
:state="app.section_features"
/>
</div>
</div>
</div>
<div v-if="app.section_features">
<div class="block-wrapper">
<img src="/assets/images/admin/main-features.jpg" alt="Main Features" class="page-image" />
</div>
<div class="block-wrapper">
<label>Title:</label>
<ValidationProvider
tag="div"
mode="passive"
name="App Title"
rules="required"
v-slot="{ errors }"
>
<input
@input="$updateText('/admin/settings', 'features_title', app.features_title)"
v-model="app.features_title"
type="text"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>Description:</label>
<ValidationProvider
tag="div"
mode="passive"
name="App Description"
rules="required"
v-slot="{ errors }"
>
<textarea
@input="
$updateText('/admin/settings', 'features_description', app.features_description)
"
rows="2"
v-model="app.features_description"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
></textarea>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
</div>
</div>
<!--Feature boxes-->
<div class="card shadow-card">
<FormLabel>Feature Boxes</FormLabel>
<div class="block-wrapper">
<div>
<div class="inline-wrapper">
<div class="switch-label">
<label class="input-label"> Show section: </label>
</div>
<SwitchInput
@input="
$updateText(
'/admin/settings',
'section_feature_boxes',
app.section_feature_boxes
)
"
v-model="app.section_feature_boxes"
class="switch"
:state="app.section_feature_boxes"
/>
</div>
</div>
</div>
<div v-if="app.section_feature_boxes">
<div class="block-wrapper">
<img src="/assets/images/admin/feature-boxes.jpg" alt="Main Features" class="page-image" />
</div>
<div class="block-wrapper">
<label>First Box Title:</label>
<ValidationProvider
tag="div"
mode="passive"
name="Feature Title 1"
rules="required"
v-slot="{ errors }"
>
<input
@input="$updateText('/admin/settings', 'feature_title_1', app.feature_title_1)"
v-model="app.feature_title_1"
type="text"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>First Box Description:</label>
<ValidationProvider
tag="div"
mode="passive"
name="Feature Description 1"
rules="required"
v-slot="{ errors }"
>
<textarea
@input="
$updateText(
'/admin/settings',
'feature_description_1',
app.feature_description_1
)
"
rows="2"
v-model="app.feature_description_1"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
></textarea>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>Second Box Title:</label>
<ValidationProvider
tag="div"
mode="passive"
name="Feature Title 2"
rules="required"
v-slot="{ errors }"
>
<input
@input="$updateText('/admin/settings', 'feature_title_2', app.feature_title_2)"
v-model="app.feature_title_2"
type="text"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>Second Box Description:</label>
<ValidationProvider
tag="div"
mode="passive"
name="Feature Description 2"
rules="required"
v-slot="{ errors }"
>
<textarea
@input="
$updateText(
'/admin/settings',
'feature_description_2',
app.feature_description_2
)
"
rows="2"
v-model="app.feature_description_2"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
></textarea>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>Third Box Title:</label>
<ValidationProvider
tag="div"
mode="passive"
name="Feature Title 3"
rules="required"
v-slot="{ errors }"
>
<input
@input="$updateText('/admin/settings', 'feature_title_3', app.feature_title_3)"
v-model="app.feature_title_3"
type="text"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>Third Box Description:</label>
<ValidationProvider
tag="div"
mode="passive"
name="Feature Description 3"
rules="required"
v-slot="{ errors }"
>
<textarea
@input="
$updateText(
'/admin/settings',
'feature_description_3',
app.feature_description_3
)
"
rows="2"
v-model="app.feature_description_3"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
></textarea>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
</div>
</div>
<!--Pricing Content-->
<div v-if="config.isSaaS" class="card shadow-card">
<FormLabel>Pricing Content</FormLabel>
<div class="block-wrapper">
<div>
<div class="inline-wrapper">
<div class="switch-label">
<label class="input-label"> Show section: </label>
</div>
<SwitchInput
@input="
$updateText(
'/admin/settings',
'section_pricing_content',
app.section_pricing_content
)
"
v-model="app.section_pricing_content"
class="switch"
:state="app.section_pricing_content"
/>
</div>
</div>
</div>
<div v-if="app.section_pricing_content">
<div class="block-wrapper">
<img
src="/assets/images/admin/pricing-content.jpg"
alt="Main Features"
class="page-image"
/>
</div>
<div class="block-wrapper">
<label>Title:</label>
<ValidationProvider
tag="div"
mode="passive"
name="App Title"
rules="required"
v-slot="{ errors }"
>
<input
@input="$updateText('/admin/settings', 'pricing_title', app.pricing_title)"
v-model="app.pricing_title"
type="text"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>Description:</label>
<ValidationProvider
tag="div"
mode="passive"
name="App Description"
rules="required"
v-slot="{ errors }"
>
<textarea
@input="
$updateText('/admin/settings', 'pricing_description', app.pricing_description)
"
rows="2"
v-model="app.pricing_description"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
></textarea>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
</div>
</div>
<!--Get Started-->
<div class="card shadow-card">
<FormLabel>Get Started Content</FormLabel>
<div class="block-wrapper">
<div>
<div class="inline-wrapper">
<div class="switch-label">
<label class="input-label"> Show section: </label>
</div>
<SwitchInput
@input="
$updateText('/admin/settings', 'section_get_started', app.section_get_started)
"
v-model="app.section_get_started"
class="switch"
:state="app.section_get_started"
/>
</div>
</div>
</div>
<div v-if="app.section_get_started">
<div class="block-wrapper">
<img
src="/assets/images/admin/get-started-content.jpg"
alt="Main Features"
class="page-image"
/>
</div>
<div class="block-wrapper">
<label>Title:</label>
<ValidationProvider
tag="div"
mode="passive"
name="App Title"
rules="required"
v-slot="{ errors }"
>
<input
@input="$updateText('/admin/settings', 'get_started_title', app.get_started_title)"
v-model="app.get_started_title"
type="text"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>Description:</label>
<ValidationProvider
tag="div"
mode="passive"
name="App Description"
rules="required"
v-slot="{ errors }"
>
<textarea
@input="
$updateText(
'/admin/settings',
'get_started_description',
app.get_started_description
)
"
rows="2"
v-model="app.get_started_description"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
></textarea>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
</div>
</div>
<!--Footer-->
<div class="card shadow-card">
<FormLabel>Footer</FormLabel>
<div class="block-wrapper">
<label>Footer content:</label>
<ValidationProvider
tag="div"
mode="passive"
name="App Title"
rules="required"
v-slot="{ errors }"
>
<input
@input="$updateText('/admin/settings', 'footer_content', app.footer_content)"
v-model="app.footer_content"
type="text"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
</div>
</div>
</PageTabGroup>
</PageTab>
</template>
<script>
import AppInputSwitch from '../../../../components/Forms/Layouts/AppInputSwitch'
import AppInputText from '../../../../components/Forms/Layouts/AppInputText'
import { ValidationProvider, ValidationObserver } from 'vee-validate/dist/vee-validate.full'
import PageTabGroup from '../../../../components/Layout/PageTabGroup'
import SelectInput from '../../../../components/Inputs/SelectInput'
import SwitchInput from '../../../../components/Inputs/SwitchInput'
import ImageInput from '../../../../components/Inputs/ImageInput'
import FormLabel from '../../../../components/UI/Labels/FormLabel'
import ButtonBase from '../../../../components/UI/Buttons/ButtonBase'
import PageTab from '../../../../components/Layout/PageTab'
import InfoBox from '../../../../components/UI/Others/InfoBox'
import { required } from 'vee-validate/dist/rules'
import axios from 'axios'
import { mapGetters } from 'vuex'
export default {
name: 'AppIndex',
components: {
AppInputSwitch,
AppInputText,
ValidationObserver,
ValidationProvider,
PageTabGroup,
SwitchInput,
SelectInput,
ImageInput,
ButtonBase,
FormLabel,
required,
PageTab,
InfoBox,
},
computed: {
...mapGetters(['config']),
},
data() {
return {
isLoading: true,
app: undefined,
}
},
mounted() {
axios
.get('/api/admin/settings', {
params: {
column: 'allow_homepage|footer_content|get_started_description|get_started_title|pricing_description|pricing_title|feature_description_3|feature_title_3|feature_description_2|feature_title_2|feature_description_1|feature_title_1|features_description|features_title|header_description|header_title|section_get_started|section_pricing_content|section_feature_boxes|section_features',
},
})
.then((response) => {
this.app = {
allow_homepage: parseInt(response.data.allow_homepage),
section_features: parseInt(response.data.section_features),
section_feature_boxes: parseInt(response.data.section_feature_boxes),
section_pricing_content: parseInt(response.data.section_pricing_content),
section_get_started: parseInt(response.data.section_get_started),
header_title: response.data.header_title,
header_description: response.data.header_description,
features_title: response.data.features_title,
features_description: response.data.features_description,
feature_title_1: response.data.feature_title_1,
feature_description_1: response.data.feature_description_1,
feature_title_2: response.data.feature_title_2,
feature_description_2: response.data.feature_description_2,
feature_title_3: response.data.feature_title_3,
feature_description_3: response.data.feature_description_3,
pricing_title: response.data.pricing_title,
pricing_description: response.data.pricing_description,
get_started_title: response.data.get_started_title,
get_started_description: response.data.get_started_description,
footer_content: response.data.footer_content,
}
})
.finally(() => {
this.isLoading = false
})
},
}
</script>
<style lang="scss" scoped>
@import '../../../../../sass/vuefilemanager/variables';
@import '../../../../../sass/vuefilemanager/mixins';
@import '../../../../../sass/vuefilemanager/forms';
.block-form {
max-width: 100%;
}
.page-image {
width: 100%;
margin: 0 auto;
display: block;
border-radius: 8px;
border: 1px solid #ececec;
}
</style>

View File

@@ -0,0 +1,481 @@
<template>
<PageTab :is-loading="isLoading">
<div v-if="app" class="card shadow-card">
<FormLabel>
{{ $t('upload_settings') }}
</FormLabel>
<AppInputText
:description="$t('admin_settings.others.upload_limit_help')"
:title="$t('admin_settings.others.upload_limit')"
>
<input
v-model="app.uploadLimit"
:placeholder="$t('admin_settings.others.upload_limit_plac')"
class="focus-border-theme input-dark"
min="0"
step="1"
type="number"
@input="$updateText('/admin/settings', 'upload_limit', app.uploadLimit, true)"
/>
</AppInputText>
<AppInputText
:description="$t('When you upload file on the server, your file will be sliced into many chunks in this size. Small size of the chunk can prevent many limits you can suffer from your server or provider. Default value is 64MB if is not set.')"
:title="$t('File Chunk Size (in MB)')"
>
<input
v-model="app.chunkSize"
:placeholder="$t('Type the chunk size in MB')"
class="focus-border-theme input-dark"
min="0"
step="1"
type="number"
@input="$updateText('/admin/settings', 'chunk_size', app.chunkSize, true)"
/>
</AppInputText>
<AppInputText
:description="$t('admin_settings.others.mimetypes_blacklist_help')"
:is-last="true"
:title="$t('admin_settings.others.mimetypes_blacklist')"
>
<textarea
v-model="app.mimetypesBlacklist"
:placeholder="$t('admin_settings.others.mimetypes_blacklist_plac')"
class="focus-border-theme input-dark"
rows="2"
type="text"
@input="$updateText('/admin/settings', 'mimetypes_blacklist', app.mimetypesBlacklist, true)"
/>
</AppInputText>
</div>
<!--Store & Upload-->
<div v-if="app" class="card shadow-card">
<FormLabel>
{{ $t('user_features') }}
</FormLabel>
<!--Available only when is not metered billing-->
<div v-if="config.subscriptionType !== 'metered'">
<AppInputSwitch
:description="$t('admin_settings.others.storage_limit_help')"
:title="$t('admin_settings.others.storage_limit')"
>
<SwitchInput
v-model="app.storageLimitation"
:state="app.storageLimitation"
class="switch"
@input="$updateText('/admin/settings', 'storage_limitation', app.storageLimitation)"
/>
</AppInputSwitch>
<AppInputText v-if="app.storageLimitation" :title="$t('admin_settings.others.default_storage')">
<input
v-model="app.defaultStorage"
:placeholder="$t('admin_settings.others.default_storage_plac')"
class="focus-border-theme input-dark"
max="999999999"
min="1"
type="number"
@input="$updateText('/admin/settings', 'default_max_storage_amount', app.defaultStorage)"
/>
</AppInputText>
</div>
<AppInputText :description="$t('zero_for_unlimited_members')" :is-last="true" :title="$t('max_team_members')">
<input
v-model="app.teamsDefaultMembers"
:placeholder="$t('admin_settings.others.default_storage_plac')"
class="focus-border-theme input-dark"
max="999999999"
min="1"
type="number"
@input="$updateText('/admin/settings', 'default_max_team_member', app.teamsDefaultMembers)"
/>
</AppInputText>
</div>
<!-- ReCaptcha -->
<div v-if="app" class="card shadow-card">
<FormLabel icon="shield">
{{ $t('reCaptcha') }}
</FormLabel>
<AppInputSwitch
:description="$t('allow_recaptcha_note')"
:is-last="!recaptcha.allowedService"
:title="$t('allow_recaptcha')"
>
<SwitchInput
v-model="recaptcha.allowedService"
:state="recaptcha.allowedService"
class="switch"
@input="$updateText('/admin/settings', 'allowed_recaptcha', recaptcha.allowedService)"
/>
</AppInputSwitch>
<div
v-if="config.isRecaptchaConfigured && recaptcha.allowedService"
:class="{ 'mb-4': recaptcha.isVisibleCredentialsForm }"
class="flex cursor-pointer items-center"
@click="recaptcha.isVisibleCredentialsForm = !recaptcha.isVisibleCredentialsForm"
>
<edit2-icon class="vue-feather text-theme mr-2" size="12" />
<b class="text-xs">{{ $t('update_your_credentials') }}</b>
</div>
<!--Set up recaptcha credentials-->
<ValidationObserver
v-if="(!config.isRecaptchaConfigured || recaptcha.isVisibleCredentialsForm) && recaptcha.allowedService"
ref="credentialsForm"
v-slot="{ invalid }"
class="rounded-xl p-5 shadow-lg"
tag="form"
@submit.prevent="storeCredentials('recaptcha')"
>
<FormLabel v-if="!config.isRecaptchaConfigured" icon="shield">
{{ $t('configure_your_credentials') }}
</FormLabel>
<ValidationProvider v-slot="{ errors }" mode="passive" name="Site Key" rules="required" tag="div">
<AppInputText :error="errors[0]" :title="$t('Site Key')">
<input
v-model="recaptcha.credentials.client_id"
:class="{ '!border-rose-600': errors[0] }"
:placeholder="$t('Paste your Site Key here')"
class="focus-border-theme input-dark"
type="text"
/>
</AppInputText>
</ValidationProvider>
<ValidationProvider v-slot="{ errors }" mode="passive" name="Secret key" rules="required" tag="div">
<AppInputText :error="errors[0]" :title="$t('Secret Key')">
<input
v-model="recaptcha.credentials.client_secret"
:class="{ '!border-rose-600': errors[0] }"
:placeholder="$t('Paste your Secret key here')"
class="focus-border-theme input-dark"
type="text"
/>
</AppInputText>
</ValidationProvider>
<ButtonBase
:disabled="isLoading"
:loading="isLoading"
button-style="theme"
class="w-full"
type="submit"
>
{{ $t('store_credentials') }}
</ButtonBase>
</ValidationObserver>
</div>
<!--Other Settings-->
<div v-if="app" class="card shadow-card">
<FormLabel>
{{ $t('application') }}
</FormLabel>
<AppInputButton
:description="$t('cache_note')"
:title="$t('cache')"
>
<ButtonBase
:disabled="isFlushingCache"
:loading="isFlushingCache"
button-style="theme"
class="w-full sm:w-auto"
@click.native="flushCache"
>
{{ $t('clear_cache') }}
</ButtonBase>
</AppInputButton>
<AppInputText :title="$t('admin_settings.others.contact_email')">
<input
v-model="app.contactMail"
:placeholder="$t('admin_settings.others.contact_email_plac')"
class="focus-border-theme input-dark"
type="email"
@input="$updateText('/admin/settings', 'contact_email', app.contactMail)"
/>
</AppInputText>
<AppInputText :is-last="true" :title="$t('admin_settings.others.google_analytics')">
<input
v-model="app.googleAnalytics"
:placeholder="$t('admin_settings.others.google_analytics_plac')"
class="focus-border-theme input-dark"
type="text"
@input="$updateText('/admin/settings', 'google_analytics', app.googleAnalytics, true)"
/>
</AppInputText>
</div>
<!--Upgrade License-->
<div v-if="app && !config.isSaaS" class="card shadow-card">
<FormLabel icon="trending-up">
{{ $t('Upgrade your License') }}
</FormLabel>
<ValidationObserver
ref="upgradeLicense"
v-slot="{ invalid }"
class="mt-6"
tag="form"
@submit.prevent="upgradeLicense"
>
<ValidationProvider
v-slot="{ errors }"
mode="passive"
name="Purchase Code"
rules="required"
tag="div"
>
<AppInputText
:error="errors[0]"
:is-last="true"
>
<div class="space-y-4 sm:flex sm:space-x-4 sm:space-y-0">
<input
v-model="purchaseCode"
:class="{ '!border-rose-600': errors[0] }"
:placeholder="$t('Paste your Purchase code here...')"
class="focus-border-theme input-dark"
type="text"
/>
<ButtonBase :loading="isLoadingUpgradingButton" button-style="theme" class="w-full sm:w-auto" type="submit">
{{ $t('Upgrade') }}
</ButtonBase>
</div>
</AppInputText>
</ValidationProvider>
</ValidationObserver>
</div>
<!-- Subscription -->
<div v-if="app && config.isSaaS" class="card shadow-card">
<FormLabel icon="credit-card">
{{ $t('subscription') }}
</FormLabel>
<AppInputText :description="$t('subscription_type_note')" :is-last="true" :title="$t('subscription_type')">
<SelectInput
:default="app.subscriptionType"
:options="subscriptionTypes"
:placeholder="$t('select_subscription_type')"
@change="subscriptionTypeChange"
/>
</AppInputText>
</div>
</PageTab>
</template>
<script>
import {Edit2Icon} from 'vue-feather-icons'
import {ValidationProvider, ValidationObserver} from 'vee-validate/dist/vee-validate.full'
import SwitchInput from '../../../../components/Inputs/SwitchInput'
import AppInputButton from '../../../../components/Forms/Layouts/AppInputButton'
import AppInputSwitch from '../../../../components/Forms/Layouts/AppInputSwitch'
import FormLabel from '../../../../components/UI/Labels/FormLabel'
import ButtonBase from '../../../../components/UI/Buttons/ButtonBase'
import AppInputText from '../../../../components/Forms/Layouts/AppInputText'
import PageTab from '../../../../components/Layout/PageTab'
import {required} from 'vee-validate/dist/rules'
import {events} from '../../../../bus'
import {mapGetters} from 'vuex'
import axios from 'axios'
import SelectInput from "../../../../components/Inputs/SelectInput";
export default {
name: 'AppOthers',
components: {
SelectInput,
AppInputButton,
ValidationObserver,
ValidationProvider,
AppInputSwitch,
AppInputText,
SwitchInput,
ButtonBase,
Edit2Icon,
FormLabel,
required,
PageTab,
},
computed: {
...mapGetters(['subscriptionTypes', 'config']),
},
data() {
return {
isLoading: true,
isLoadingUpgradingButton: false,
isFlushingCache: false,
app: undefined,
purchaseCode: undefined,
recaptcha: {
allowedService: false,
isVisibleCredentialsForm: false,
credentials: {
key: undefined,
secret: undefined,
},
},
}
},
methods: {
async upgradeLicense() {
this.isLoadingUpgradingButton = true
// Validate fields
const isValid = await this.$refs.upgradeLicense.validate()
if (!isValid) return
axios.post('/api/admin/upgrade-license', {
purchaseCode: this.purchaseCode
})
.then((response) => {
this.$store.dispatch('getLanguageTranslations', this.config.locale)
this.$store.commit('REPLACE_CONFIG_VALUE', {
key: 'isSaaS',
value: true,
})
events.$emit('toaster', {
type: 'success',
message: this.$t('Your license was successfully upgraded'),
})
})
.catch((error) => {
if (error.response.status === 400) {
events.$emit('alert:open', {
title: this.$t('Purchase code is invalid or is not Extended License'),
})
} else {
events.$emit('alert:open', {
title: this.$t('popup_error.title'),
message: this.$t('popup_error.message'),
})
}
})
.finally(() => {
this.isLoadingUpgradingButton = false
})
},
subscriptionTypeChange(type) {
events.$emit('confirm:open', {
title: this.$t('subscription_type_change_warn'),
message: this.$t('subscription_type_change_warn_description'),
action: {
type: type,
operation: 'change-subscription-type',
},
})
},
async storeCredentials(service) {
// Validate fields
const isValid = await this.$refs.credentialsForm.validate()
if (!isValid) return
// Start loading
this.isLoading = true
// Send request to get verify account
axios
.post('/api/admin/settings/social-service', {
client_id: this[service].credentials.client_id,
client_secret: this[service].credentials.client_secret,
service: service,
})
.then(() => {
// Commit credentials
this.$store.commit('SET_SOCIAL_LOGIN_CONFIGURED', service)
this[service].allowedService = true
this[service].isVisibleCredentialsForm = false
// Show toaster
events.$emit('toaster', {
type: 'success',
message: this.$t('toaster.credentials_set', {
service: service,
}),
})
})
.catch((error) => {
if (error.response.status === 500) {
this.isError = true
this.errorMessage = error.response.data.message
}
})
.finally(() => (this.isLoading = false))
},
flushCache() {
this.isFlushingCache = true
axios
.get('/api/admin/settings/flush-cache')
.then(() => {
events.$emit('toaster', {
type: 'success',
message: 'Your cache was successfully deleted.',
})
})
.finally(() => {
this.isFlushingCache = false
})
},
},
mounted() {
this.recaptcha.allowedService = this.config.allowedRecaptcha
axios
.get('/api/admin/settings', {
params: {
column: 'contact_email|google_analytics|default_max_storage_amount|storage_limitation|mimetypes_blacklist|upload_limit|subscriptionType|chunk_size|default_max_team_member',
},
})
.then((response) => {
this.isLoading = false
this.app = {
contactMail: response.data.contact_email,
googleAnalytics: response.data.google_analytics,
defaultStorage: response.data.default_max_storage_amount,
storageLimitation: parseInt(response.data.storage_limitation),
mimetypesBlacklist: response.data.mimetypes_blacklist,
uploadLimit: response.data.upload_limit,
subscriptionType: response.data.subscriptionType,
chunkSize: response.data.chunk_size,
teamsDefaultMembers: response.data.default_max_team_member,
}
})
},
created() {
events.$on('action:confirmed', (data) => {
if (data.operation === 'change-subscription-type') {
// Update database
this.$updateText('/admin/settings', 'subscription_type', data.type)
// Update config
this.$store.commit('REPLACE_CONFIG_VALUE', {
key: 'subscriptionType',
value: data.type,
})
}
})
},
destroyed() {
events.$off('action:confirmed')
},
}
</script>

View File

@@ -0,0 +1,274 @@
<template>
<PageTab v-if="!isLoading">
<!--Cron check-->
<div class="card shadow-card">
<FormLabel icon="info">Cron</FormLabel>
<div class="lg:flex lg:space-y-0 space-y-3 items-center justify-between">
<div class="text-left">
<b class="block text-sm font-bold">Cron Jobs</b>
<small v-if="!cron.running" class="text-xs text-gray-600 pt-1 block leading-normal">
We detect, your cron jobs probably doesn't work correctly, please check it.
</small>
<small v-if="cron.running" class="text-xs text-gray-600 pt-1 block leading-normal">
Latest Update: {{ cron.lastUpdate }}
</small>
</div>
<div class="flex items-center">
<check-icon v-if="cron.running" size="16" class="vue-feather text-green-600 dark:text-green-600" />
<x-icon v-if="!cron.running" size="16" class="vue-feather text-red-600 dark:text-red-600" />
<span
class="ml-3 text-sm font-bold"
:class="cron.running ? 'text-green-600 dark:text-green-600' : 'text-red-600 dark:text-red-600'"
>
{{ cron.running ? 'Working correctly' : "Doesn't work" }}
</span>
</div>
</div>
</div>
<!--Logs-->
<div class="card shadow-card">
<FormLabel icon="list">Latest Server Logs</FormLabel>
<InfoBox v-if="!logs.length" class="!mb-0">
<p v-html="$t('there_is_not_log')"></p>
</InfoBox>
<div
v-if="logs.length"
v-for="(log, i) in logs"
:key="i"
class="flex items-center justify-between border-b border-dashed border-light py-3 dark:border-opacity-5"
>
<div class="text-left">
<b class="block text-sm font-bold">
{{ log }}
</b>
</div>
<div @click="downloadLog(log)" class="flex h-8 w-8 items-center justify-center rounded-md bg-light-background transition-colors hover:bg-green-100 dark:bg-2x-dark-foreground cursor-pointer">
<DownloadCloudIcon size="15" class="opacity-75" />
</div>
</div>
</div>
<!--Database Backups check-->
<div class="card shadow-card">
<FormLabel icon="database"> Latest Database Backups </FormLabel>
<InfoBox v-if="!backups.length" class="!mb-0">
<p v-html="$t('there_is_not_database_backup')"></p>
</InfoBox>
<InfoBox v-if="backups.length" class="!mb-3">
<p v-html="$t('backup_path')"></p>
</InfoBox>
<div
v-if="backups.length"
v-for="(filename, i) in backups"
:key="i"
class="md:flex md:space-y-0 space-y-3 items-center justify-between border-b border-dashed border-light py-3 dark:border-opacity-5"
>
<div class="text-left">
<b class="block text-sm font-bold">{{ filename }}</b>
</div>
<div class="flex items-center">
<check-icon size="16" class="vue-feather text-green-600 dark:text-green-600" />
<span class="ml-3 text-sm font-bold text-green-600 dark:text-green-600"> Stored Successfully </span>
</div>
</div>
</div>
<!--Writable directories-->
<div class="card shadow-card">
<FormLabel icon="database">Writable Permission</FormLabel>
<div
v-for="(isWritable, file, i) in writable"
:key="i"
class="flex md:space-y-0 space-y-3 items-center justify-between border-b border-dashed border-light py-3 dark:border-opacity-5"
>
<div class="text-left">
<b class="block text-sm font-bold">/{{ file }}</b>
</div>
<div class="flex items-center">
<check-icon v-if="isWritable" size="16" class="vue-feather text-green-600 dark:text-green-600" />
<x-icon v-if="!isWritable" size="16" class="vue-feather text-red-600 dark:text-red-600" />
<span :class="{'text-green-600 dark:text-green-600': isWritable, 'text-red-600 dark:text-red-600': !isWritable}" class="ml-3 text-sm font-bold ">
{{ isWritable ? 'Writable' : 'Unwritable'}}
</span>
</div>
</div>
</div>
<!--PHP version and ini check-->
<div class="card shadow-card">
<FormLabel icon="info"> PHP Settings </FormLabel>
<div
class="flex items-center justify-between border-b border-dashed border-light py-3 dark:border-opacity-5"
>
<div class="text-left">
<b class="block text-sm font-bold">PHP Version</b>
<small v-if="!phpVersion.acceptable" class="text-xs text-gray-600 pt-1 block leading-normal">
You need PHP version at least {{ phpVersion.minimal }}.
</small>
</div>
<div class="flex items-center">
<check-icon
v-if="phpVersion.acceptable"
size="16"
class="vue-feather text-green-600 dark:text-green-600"
/>
<x-icon
v-if="!phpVersion.acceptable"
size="16"
class="vue-feather text-red-600 dark:text-red-600"
/>
<span
class="ml-3 text-sm font-bold"
:class="
phpVersion.acceptable
? 'text-green-600 dark:text-green-600'
: 'text-red-600 dark:text-red-600'
"
>
{{ phpVersion.current }}
</span>
</div>
</div>
<div
v-for="(values, setting, i) in ini"
:key="i"
class="flex items-center justify-between border-b border-dashed border-light py-3 dark:border-opacity-5"
>
<div class="text-left">
<b class="block text-sm font-bold">{{ setting }}</b>
<small v-if="!values.status" class="text-xs text-gray-600 pt-1 block leading-normal">
You must set this value at least {{ values.minimal }}.
</small>
</div>
<div class="flex items-center">
<check-icon v-if="values.status" size="16" class="vue-feather text-green-600 dark:text-green-600" />
<x-icon v-if="!values.status" size="16" class="vue-feather text-red-600 dark:text-red-600" />
<span
class="ml-3 text-sm font-bold"
:class="values.status ? 'text-green-600 dark:text-green-600' : 'text-red-600 dark:text-red-600'"
>
{{ values.current }}{{ setting !== 'max_execution_time' ? 'M' : '' }}
</span>
</div>
</div>
</div>
<!--PHP Extension info-->
<div class="card shadow-card">
<FormLabel icon="info"> PHP Extensions </FormLabel>
<div
v-if="modules"
v-for="(value, module, i) in modules"
:key="i"
class="flex items-center justify-between border-b border-dashed border-light py-3 dark:border-opacity-5"
>
<b class="text-sm font-bold">
{{ module }}
</b>
<div class="flex items-center">
<check-icon v-if="value" size="16" class="vue-feather text-green-600 dark:text-green-600" />
<x-icon v-if="!value" size="16" class="vue-feather text-red-600 dark:text-red-600" />
<span
class="ml-3 text-sm font-bold"
:class="value ? 'text-green-600 dark:text-green-600' : 'text-red-600 dark:text-red-600'"
>
{{ value ? 'Module Installed' : 'You have to install this module' }}
</span>
</div>
</div>
</div>
</PageTab>
</template>
<script>
import { CheckIcon, XIcon, DownloadCloudIcon } from 'vue-feather-icons'
import FormLabel from '../../../../components/UI/Labels/FormLabel'
import PageTab from '../../../../components/Layout/PageTab'
import InfoBox from '../../../../components/UI/Others/InfoBox'
import { mapGetters } from 'vuex'
import axios from 'axios'
export default {
name: 'Server',
components: {
FormLabel,
InfoBox,
PageTab,
DownloadCloudIcon,
CheckIcon,
XIcon,
},
computed: {
...mapGetters(['config']),
isCheckedAPI() {
return typeof this.apiRunning !== 'undefined'
},
},
data() {
return {
isLoading: true,
ini: undefined,
cron: undefined,
modules: undefined,
phpVersion: undefined,
apiRunning: undefined,
backups: undefined,
logs: undefined,
writable: undefined,
}
},
methods: {
downloadLog(log) {
let anchor = document.createElement('a')
anchor.href = `/admin/log/${log}`
anchor.download = log
document.body.appendChild(anchor)
anchor.click()
}
},
created() {
// Get status
axios.get('/api/admin/status').then((response) => {
this.isLoading = false
this.ini = response.data.ini
this.cron = response.data.cron
this.modules = response.data.modules
this.phpVersion = response.data.php_version
this.backups = response.data.backups
this.logs = response.data.logs
this.writable = response.data.writable
})
// Ping API
axios
.get('/api/ping')
.then((response) => {
this.apiRunning = response.data === 'pong'
})
.catch(() => {
this.apiRunning = false
})
},
}
</script>

View File

@@ -0,0 +1,411 @@
<template>
<PageTab>
<!--User Login/Registration-->
<div v-if="app" class="card shadow-card">
<FormLabel>
{{ $t('User Login/Registration') }}
</FormLabel>
<AppInputSwitch
:title="$t('admin_settings.others.allow_registration')"
:description="$t('admin_settings.others.allow_registration_help')"
>
<SwitchInput
@input="$updateText('/admin/settings', 'registration', app.userRegistration)"
v-model="app.userRegistration"
class="switch"
:state="app.userRegistration"
/>
</AppInputSwitch>
<AppInputSwitch
:title="$t('require_email_verification')"
:description="$t('require_email_verification_note')"
:is-last="true"
>
<SwitchInput
@input="$updateText('/admin/settings', 'user_verification', app.userVerification)"
v-model="app.userVerification"
class="switch"
:state="app.userVerification"
/>
</AppInputSwitch>
</div>
<!--Facebook Social Authentication-->
<div class="card shadow-card">
<img :src="$getSocialLogo('facebook')" alt="Facebook" class="mb-8 h-5" />
<AppInputSwitch
:title="$t('Allow Login via Facebook')"
:description="$t('You users will be able to login via Facebook account.')"
:is-last="!facebook.allowedService"
>
<SwitchInput
@input="$updateText('/admin/settings', 'allowed_facebook_login', facebook.allowedService)"
v-model="facebook.allowedService"
class="switch"
:state="facebook.allowedService"
/>
</AppInputSwitch>
<AppInputText
v-if="facebook.allowedService"
:title="$t('Your Callback URL')"
:description="$t('Please copy your url and paste it to the service callback URL.')"
>
<CopyInput size="small" :str="getCallbackEndpoint('facebook')" />
</AppInputText>
<div
v-if="config.isFacebookLoginConfigured && facebook.allowedService"
@click="facebook.isVisibleCredentialsForm = !facebook.isVisibleCredentialsForm"
class="flex cursor-pointer items-center"
:class="{ 'mb-4': facebook.isVisibleCredentialsForm }"
>
<edit2-icon size="12" class="vue-feather text-theme mr-2" />
<b class="text-xs">{{ $t('update_your_credentials') }}</b>
</div>
<!--Set up facebook credentials-->
<ValidationObserver
v-if="
(!config.isFacebookLoginConfigured || facebook.isVisibleCredentialsForm) && facebook.allowedService
"
@submit.prevent="storeCredentials('facebook')"
ref="facebook"
v-slot="{ invalid }"
tag="form"
class="rounded-xl p-5 shadow-lg"
>
<FormLabel v-if="!config.isFacebookLoginConfigured" icon="shield">
{{ $t('Configure Credentials') }}
</FormLabel>
<ValidationProvider tag="div" mode="passive" name="Client ID" rules="required" v-slot="{ errors }">
<AppInputText :title="$t('Client ID')" :error="errors[0]">
<input
v-model="facebook.credentials.client_id"
:placeholder="$t('Paste your Client ID here')"
type="text"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
/>
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Client Secret" rules="required" v-slot="{ errors }">
<AppInputText :title="$t('Client Secret')" :error="errors[0]">
<input
v-model="facebook.credentials.client_secret"
:placeholder="$t('Paste your Client Secret here')"
type="text"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
/>
</AppInputText>
</ValidationProvider>
<ButtonBase
:disabled="isLoading"
:loading="isLoading"
button-style="theme"
type="submit"
class="w-full"
>
{{ $t('store_credentials') }}
</ButtonBase>
</ValidationObserver>
</div>
<!--Google Social Authentication-->
<div class="card shadow-card">
<img :src="$getSocialLogo('google')" alt="Google" class="mb-8 h-7" />
<AppInputSwitch
:title="$t('Allow Login via Google')"
:description="$t('You users will be able to login via Google account.')"
:is-last="!google.allowedService"
>
<SwitchInput
@input="$updateText('/admin/settings', 'allowed_google_login', google.allowedService)"
v-model="google.allowedService"
class="switch"
:state="google.allowedService"
/>
</AppInputSwitch>
<AppInputText
v-if="google.allowedService"
:title="$t('Your Callback URL')"
:description="$t('Please copy your url and paste it to the service callback URL.')"
>
<CopyInput size="small" :str="getCallbackEndpoint('google')" />
</AppInputText>
<div
v-if="config.isGoogleLoginConfigured && google.allowedService"
@click="google.isVisibleCredentialsForm = !google.isVisibleCredentialsForm"
class="flex cursor-pointer items-center"
:class="{ 'mb-4': google.isVisibleCredentialsForm }"
>
<edit2-icon size="12" class="vue-feather text-theme mr-2" />
<b class="text-xs">{{ $t('update_your_credentials') }}</b>
</div>
<!--Set up Google credentials-->
<ValidationObserver
v-if="(!config.isGoogleLoginConfigured || google.isVisibleCredentialsForm) && google.allowedService"
@submit.prevent="storeCredentials('google')"
ref="google"
v-slot="{ invalid }"
tag="form"
class="rounded-xl p-5 shadow-lg"
>
<FormLabel v-if="!config.isGoogleLoginConfigured" icon="shield">
{{ $t('Configure Credentials') }}
</FormLabel>
<ValidationProvider tag="div" mode="passive" name="Client ID" rules="required" v-slot="{ errors }">
<AppInputText :title="$t('Client ID')" :error="errors[0]">
<input
v-model="google.credentials.client_id"
:placeholder="$t('Paste your Client ID here')"
type="text"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
/>
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Client Secret" rules="required" v-slot="{ errors }">
<AppInputText :title="$t('Client Secret')" :error="errors[0]">
<input
v-model="google.credentials.client_secret"
:placeholder="$t('Paste your Client Secret here')"
type="text"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
/>
</AppInputText>
</ValidationProvider>
<ButtonBase
:disabled="isLoading"
:loading="isLoading"
button-style="theme"
type="submit"
class="w-full"
>
{{ $t('store_credentials') }}
</ButtonBase>
</ValidationObserver>
</div>
<!--Github Social Authentication-->
<div class="card shadow-card">
<img :src="$getSocialLogo('github')" alt="Github" class="mb-8 h-5" />
<AppInputSwitch
:title="$t('Allow Login via GitHub')"
:description="$t('You users will be able to login via GitHub account.')"
:is-last="!github.allowedService"
>
<SwitchInput
@input="$updateText('/admin/settings', 'allowed_github_login', github.allowedService)"
v-model="github.allowedService"
class="switch"
:state="github.allowedService"
/>
</AppInputSwitch>
<AppInputText
v-if="github.allowedService"
:title="$t('Your Callback URL')"
:description="$t('Please copy your url and paste it to the service callback URL.')"
>
<CopyInput size="small" :str="getCallbackEndpoint('github')" />
</AppInputText>
<div
v-if="config.isGithubLoginConfigured && github.allowedService"
@click="github.isVisibleCredentialsForm = !github.isVisibleCredentialsForm"
class="flex cursor-pointer items-center"
:class="{ 'mb-4': github.isVisibleCredentialsForm }"
>
<edit2-icon size="12" class="vue-feather text-theme mr-2" />
<b class="text-xs">{{ $t('update_your_credentials') }}</b>
</div>
<!--Set up github credentials-->
<ValidationObserver
v-if="(!config.isGithubLoginConfigured || github.isVisibleCredentialsForm) && github.allowedService"
@submit.prevent="storeCredentials('github')"
ref="github"
v-slot="{ invalid }"
tag="form"
class="rounded-xl p-5 shadow-lg"
>
<FormLabel v-if="!config.isGithubLoginConfigured" icon="shield">
{{ $t('Configure Credentials') }}
</FormLabel>
<ValidationProvider tag="div" mode="passive" name="Client ID" rules="required" v-slot="{ errors }">
<AppInputText :title="$t('Client ID')" :error="errors[0]">
<input
v-model="github.credentials.client_id"
:placeholder="$t('Paste your Client ID here')"
type="text"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
/>
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Client Secret" rules="required" v-slot="{ errors }">
<AppInputText :title="$t('Client Secret')" :error="errors[0]">
<input
v-model="github.credentials.client_secret"
:placeholder="$t('Paste your Client Secret here')"
type="text"
:class="{ '!border-rose-600': errors[0] }"
class="focus-border-theme input-dark"
/>
</AppInputText>
</ValidationProvider>
<ButtonBase
:disabled="isLoading"
:loading="isLoading"
button-style="theme"
type="submit"
class="w-full"
>
{{ $t('store_credentials') }}
</ButtonBase>
</ValidationObserver>
</div>
</PageTab>
</template>
<script>
import { Edit2Icon } from 'vue-feather-icons'
import { ValidationProvider, ValidationObserver } from 'vee-validate/dist/vee-validate.full'
import SwitchInput from '../../../../components/Inputs/SwitchInput'
import AppInputSwitch from '../../../../components/Forms/Layouts/AppInputSwitch'
import CopyInput from '../../../../components/Inputs/CopyInput'
import FormLabel from '../../../../components/UI/Labels/FormLabel'
import ButtonBase from '../../../../components/UI/Buttons/ButtonBase'
import AppInputText from '../../../../components/Forms/Layouts/AppInputText'
import PageTab from '../../../../components/Layout/PageTab'
import { required } from 'vee-validate/dist/rules'
import { events } from '../../../../bus'
import { mapGetters } from 'vuex'
import axios from 'axios'
export default {
name: 'SignInUp',
components: {
CopyInput,
ValidationObserver,
ValidationProvider,
AppInputSwitch,
AppInputText,
SwitchInput,
ButtonBase,
Edit2Icon,
FormLabel,
required,
PageTab,
events,
},
computed: {
...mapGetters(['config']),
},
data() {
return {
isLoading: false,
app: {
userRegistration: undefined,
userVerification: undefined,
},
facebook: {
allowedService: false,
isVisibleCredentialsForm: false,
credentials: {
key: undefined,
secret: undefined,
},
},
google: {
allowedService: false,
isVisibleCredentialsForm: false,
credentials: {
key: undefined,
secret: undefined,
},
},
github: {
allowedService: false,
isVisibleCredentialsForm: false,
credentials: {
key: undefined,
secret: undefined,
},
},
}
},
methods: {
getCallbackEndpoint(service) {
return `${this.config.host}/socialite/${service}/callback`
},
async storeCredentials(service) {
// Validate fields
const isValid = await this.$refs[service].validate()
if (!isValid) return
// Start loading
this.isLoading = true
// Send request to get verify account
axios
.post('/api/admin/settings/social-service', {
client_id: this[service].credentials.client_id,
client_secret: this[service].credentials.client_secret,
service: service,
})
.then(() => {
// Commit credentials
this.$store.commit('SET_SOCIAL_LOGIN_CONFIGURED', service)
this[service].allowedService = true
this[service].isVisibleCredentialsForm = false
// Show toaster
events.$emit('toaster', {
type: 'success',
message: this.$t('toaster.credentials_set', {
service: service,
}),
})
})
.catch((error) => {
if (error.response.status === 500) {
this.isError = true
this.errorMessage = error.response.data.message
}
})
.finally(() => (this.isLoading = false))
},
},
created() {
this.facebook.allowedService = this.config.allowedFacebookLogin
this.google.allowedService = this.config.allowedGoogleLogin
this.github.allowedService = this.config.allowedGithubLogin
this.app.userRegistration = this.config.userRegistration
this.app.userVerification = this.config.userVerification
},
}
</script>