feat: scheduler history + retries + admin scheduler page (wip)

pull/5733/head
Nicolas Giard 2 years ago
parent 39b273b224
commit 24ddab73fc
No known key found for this signature in database
GPG Key ID: 85061B8F9D55B7C8

@ -33,6 +33,8 @@ defaults:
workers: 3 workers: 3
pollingCheck: 5 pollingCheck: 5
scheduledCheck: 300 scheduledCheck: 300
maxRetries: 5
retryBackoff: 60
# DB defaults # DB defaults
api: api:
isEnabled: false isEnabled: false

@ -55,12 +55,13 @@ module.exports = {
WIKI.logger.info('Scheduler: [ STARTED ]') WIKI.logger.info('Scheduler: [ STARTED ]')
}, },
async addJob ({ task, payload, waitUntil, isScheduled = false, notify = true }) { async addJob ({ task, payload, waitUntil, maxRetries, isScheduled = false, notify = true }) {
try { try {
await WIKI.db.knex('jobs').insert({ await WIKI.db.knex('jobs').insert({
task, task,
useWorker: !(typeof this.tasks[task] === 'function'), useWorker: !(typeof this.tasks[task] === 'function'),
payload, payload,
maxRetries: maxRetries ?? WIKI.config.scheduler.maxRetries,
isScheduled, isScheduled,
waitUntil, waitUntil,
createdBy: WIKI.INSTANCE_ID createdBy: WIKI.INSTANCE_ID
@ -76,6 +77,7 @@ module.exports = {
} }
}, },
async processJob () { async processJob () {
let jobId = null
try { try {
await WIKI.db.knex.transaction(async trx => { await WIKI.db.knex.transaction(async trx => {
const jobs = await trx('jobs') const jobs = await trx('jobs')
@ -85,6 +87,23 @@ module.exports = {
if (jobs && jobs.length === 1) { if (jobs && jobs.length === 1) {
const job = jobs[0] const job = jobs[0]
WIKI.logger.info(`Processing new job ${job.id}: ${job.task}...`) WIKI.logger.info(`Processing new job ${job.id}: ${job.task}...`)
jobId = job.id
// -> Add to Job History
await WIKI.db.knex('jobHistory').insert({
id: job.id,
task: job.task,
state: 'active',
useWorker: job.useWorker,
wasScheduled: job.isScheduled,
payload: job.payload,
attempt: job.retries + 1,
maxRetries: job.maxRetries,
createdAt: job.createdAt
}).onConflict('id').merge({
startedAt: new Date()
})
// -> Start working on it
try {
if (job.useWorker) { if (job.useWorker) {
await this.workerPool.execute({ await this.workerPool.execute({
id: job.id, id: job.id,
@ -94,10 +113,48 @@ module.exports = {
} else { } else {
await this.tasks[job.task](job.payload) await this.tasks[job.task](job.payload)
} }
// -> Update job history (success)
await WIKI.db.knex('jobHistory').where({
id: job.id
}).update({
state: 'completed',
completedAt: new Date()
})
WIKI.logger.info(`Completed job ${job.id}: ${job.task} [ SUCCESS ]`)
} catch (err) {
WIKI.logger.warn(`Failed to complete job ${job.id}: ${job.task} [ FAILED ]`)
WIKI.logger.warn(err)
// -> Update job history (fail)
await WIKI.db.knex('jobHistory').where({
id: job.id
}).update({
state: 'failed',
lastErrorMessage: err.message
})
// -> Reschedule for retry
if (job.retries < job.maxRetries) {
const backoffDelay = (2 ** job.retries) * WIKI.config.scheduler.retryBackoff
await trx('jobs').insert({
...job,
retries: job.retries + 1,
waitUntil: DateTime.utc().plus({ seconds: backoffDelay }).toJSDate(),
updatedAt: new Date()
})
WIKI.logger.warn(`Rescheduling new attempt for job ${job.id}: ${job.task}...`)
}
}
} }
}) })
} catch (err) { } catch (err) {
WIKI.logger.warn(err) WIKI.logger.warn(err)
if (jobId) {
WIKI.db.knex('jobHistory').where({
id: jobId
}).update({
state: 'interrupted',
lastErrorMessage: err.message
})
}
} }
}, },
async addScheduled () { async addScheduled () {

@ -125,12 +125,16 @@ exports.up = async knex => {
.createTable('jobHistory', table => { .createTable('jobHistory', table => {
table.uuid('id').notNullable().primary() table.uuid('id').notNullable().primary()
table.string('task').notNullable() table.string('task').notNullable()
table.string('state').notNullable() table.enum('state', ['active', 'completed', 'failed', 'interrupted']).notNullable()
table.boolean('useWorker').notNullable().defaultTo(false)
table.boolean('wasScheduled').notNullable().defaultTo(false)
table.jsonb('payload') table.jsonb('payload')
table.string('lastErrorMessage') table.integer('attempt').notNullable().defaultTo(1)
table.integer('maxRetries').notNullable().defaultTo(0)
table.text('lastErrorMessage')
table.timestamp('createdAt').notNullable() table.timestamp('createdAt').notNullable()
table.timestamp('startedAt').notNullable() table.timestamp('startedAt').notNullable().defaultTo(knex.fn.now())
table.timestamp('completedAt').notNullable().defaultTo(knex.fn.now()) table.timestamp('completedAt')
}) })
// JOB SCHEDULE ------------------------ // JOB SCHEDULE ------------------------
.createTable('jobSchedule', table => { .createTable('jobSchedule', table => {
@ -154,6 +158,8 @@ exports.up = async knex => {
table.string('task').notNullable() table.string('task').notNullable()
table.boolean('useWorker').notNullable().defaultTo(false) table.boolean('useWorker').notNullable().defaultTo(false)
table.jsonb('payload') table.jsonb('payload')
table.integer('retries').notNullable().defaultTo(0)
table.integer('maxRetries').notNullable().defaultTo(0)
table.timestamp('waitUntil') table.timestamp('waitUntil')
table.boolean('isScheduled').notNullable().defaultTo(false) table.boolean('isScheduled').notNullable().defaultTo(false)
table.string('createdBy') table.string('createdBy')

@ -7,7 +7,6 @@ const path = require('path')
const fs = require('fs-extra') const fs = require('fs-extra')
const { DateTime } = require('luxon') const { DateTime } = require('luxon')
const graphHelper = require('../../helpers/graph') const graphHelper = require('../../helpers/graph')
const cronParser = require('cron-parser')
module.exports = { module.exports = {
Query: { Query: {
@ -28,33 +27,34 @@ module.exports = {
return WIKI.config.security return WIKI.config.security
}, },
async systemJobs (obj, args) { async systemJobs (obj, args) {
switch (args.type) { switch (args.state) {
case 'ACTIVE': { case 'ACTIVE': {
// const result = await WIKI.scheduler.boss.fetch('*', 25, { includeMeta: true }) // const result = await WIKI.scheduler.boss.fetch('*', 25, { includeMeta: true })
return [] return []
} }
case 'COMPLETED': { case 'COMPLETED': {
const result = await WIKI.scheduler.boss.fetchCompleted('*', 25, { includeMeta: true }) return []
console.info(result) }
return result ?? [] case 'FAILED': {
return []
}
case 'INTERRUPTED': {
return []
} }
default: { default: {
WIKI.logger.warn('Invalid Job Type requested.') WIKI.logger.warn('Invalid Job State requested.')
return [] return []
} }
} }
}, },
async systemScheduledJobs (obj, args) { async systemJobsScheduled (obj, args) {
const jobs = await WIKI.scheduler.boss.getSchedules() return WIKI.db.knex('jobSchedule').orderBy('task')
return jobs.map(job => ({ },
id: job.name, async systemJobsUpcoming (obj, args) {
name: job.name, return WIKI.db.knex('jobs').orderBy([
cron: job.cron, { column: 'waitUntil', order: 'asc', nulls: 'first' },
timezone: job.timezone, { column: 'createdAt', order: 'asc' }
nextExecution: cronParser.parseExpression(job.cron, { tz: job.timezone }).next(), ])
createdAt: job.created_on,
updatedAt: job.updated_on
}))
} }
}, },
Mutation: { Mutation: {
@ -123,15 +123,19 @@ module.exports = {
httpsPort () { httpsPort () {
return WIKI.servers.servers.https ? _.get(WIKI.servers.servers.https.address(), 'port', 0) : 0 return WIKI.servers.servers.https ? _.get(WIKI.servers.servers.https.address(), 'port', 0) : 0
}, },
isMailConfigured () {
return WIKI.config?.mail?.host?.length > 2
},
async isSchedulerHealthy () {
const results = await WIKI.db.knex('jobHistory').count('* as total').whereIn('state', ['failed', 'interrupted']).andWhere('startedAt', '>=', DateTime.utc().minus({ days: 1 }).toISO()).first()
return _.toSafeInteger(results?.total) === 0
},
latestVersion () { latestVersion () {
return WIKI.system.updates.version return WIKI.system.updates.version
}, },
latestVersionReleaseDate () { latestVersionReleaseDate () {
return DateTime.fromISO(WIKI.system.updates.releaseDate).toJSDate() return DateTime.fromISO(WIKI.system.updates.releaseDate).toJSDate()
}, },
mailConfigured () {
return WIKI.config?.mail?.host?.length > 2
},
nodeVersion () { nodeVersion () {
return process.version.substr(1) return process.version.substr(1)
}, },
@ -168,12 +172,6 @@ module.exports = {
sslSubscriberEmail () { sslSubscriberEmail () {
return WIKI.config.ssl.enabled && WIKI.config.ssl.provider === 'letsencrypt' ? WIKI.config.ssl.subscriberEmail : null return WIKI.config.ssl.enabled && WIKI.config.ssl.provider === 'letsencrypt' ? WIKI.config.ssl.subscriberEmail : null
}, },
telemetry () {
return WIKI.telemetry.enabled
},
telemetryClientId () {
return WIKI.config.telemetry.clientId
},
async upgradeCapable () { async upgradeCapable () {
return !_.isNil(process.env.UPGRADE_COMPANION) return !_.isNil(process.env.UPGRADE_COMPANION)
}, },

@ -8,9 +8,10 @@ extend type Query {
systemInfo: SystemInfo systemInfo: SystemInfo
systemSecurity: SystemSecurity systemSecurity: SystemSecurity
systemJobs( systemJobs(
type: SystemJobType! state: SystemJobState
): [SystemJob] ): [SystemJob]
systemScheduledJobs: [SystemScheduledJob] systemJobsScheduled: [SystemJobScheduled]
systemJobsUpcoming: [SystemJobUpcoming]
} }
extend type Mutation { extend type Mutation {
@ -72,9 +73,10 @@ type SystemInfo {
httpPort: Int httpPort: Int
httpRedirection: Boolean httpRedirection: Boolean
httpsPort: Int httpsPort: Int
isMailConfigured: Boolean
isSchedulerHealthy: Boolean
latestVersion: String latestVersion: String
latestVersionReleaseDate: Date latestVersionReleaseDate: Date
mailConfigured: Boolean
nodeVersion: String nodeVersion: String
operatingSystem: String operatingSystem: String
pagesTotal: Int pagesTotal: Int
@ -86,8 +88,6 @@ type SystemInfo {
sslStatus: String sslStatus: String
sslSubscriberEmail: String sslSubscriberEmail: String
tagsTotal: Int tagsTotal: Int
telemetry: Boolean
telemetryClientId: String
upgradeCapable: Boolean upgradeCapable: Boolean
usersTotal: Int usersTotal: Int
workingDirectory: String workingDirectory: String
@ -151,22 +151,46 @@ enum SystemSecurityCorsMode {
type SystemJob { type SystemJob {
id: UUID id: UUID
name: String task: String
priority: Int state: SystemJobState
state: String useWorker: Boolean
wasScheduled: Boolean
payload: JSON
attempt: Int
maxRetries: Int
lastErrorMessage: String
createdAt: Date
startedAt: Date
completedAt: Date
} }
type SystemScheduledJob { type SystemJobScheduled {
id: String id: UUID
name: String task: String
cron: String cron: String
timezone: String type: String
nextExecution: Date payload: JSON
createdAt: Date
updatedAt: Date
}
type SystemJobUpcoming {
id: UUID
task: String
useWorker: Boolean
payload: JSON
retries: Int
maxRetries: Int
waitUntil: Date
isScheduled: Boolean
createdBy: String
createdAt: Date createdAt: Date
updatedAt: Date updatedAt: Date
} }
enum SystemJobType { enum SystemJobState {
ACTIVE ACTIVE
COMPLETED COMPLETED
FAILED
INTERRUPTED
} }

@ -36,5 +36,11 @@
"dist", "dist",
".quasar", ".quasar",
"node_modules" "node_modules"
],
"vueCompilerOptions": {
"target": 3,
"plugins": [
"@volar/vue-language-plugin-pug"
] ]
} }
}

@ -89,6 +89,7 @@
"@intlify/vite-plugin-vue-i18n": "6.0.1", "@intlify/vite-plugin-vue-i18n": "6.0.1",
"@quasar/app-vite": "1.0.6", "@quasar/app-vite": "1.0.6",
"@types/lodash": "4.14.184", "@types/lodash": "4.14.184",
"@volar/vue-language-plugin-pug": "1.0.1",
"browserlist": "latest", "browserlist": "latest",
"eslint": "8.22.0", "eslint": "8.22.0",
"eslint-config-standard": "17.0.0", "eslint-config-standard": "17.0.0",

@ -1515,7 +1515,15 @@
"admin.scheduler.completedNone": "There are no recently completed job to display.", "admin.scheduler.completedNone": "There are no recently completed job to display.",
"admin.scheduler.scheduledNone": "There are no scheduled jobs at the moment.", "admin.scheduler.scheduledNone": "There are no scheduled jobs at the moment.",
"admin.scheduler.cron": "Cron", "admin.scheduler.cron": "Cron",
"admin.scheduler.nextExecutionIn": "Next run {date}", "admin.scheduler.createdBy": "by instance {instance}",
"admin.scheduler.nextExecution": "Next Run", "admin.scheduler.upcoming": "Upcoming",
"admin.scheduler.timezone": "Timezone" "admin.scheduler.failed": "Failed",
"admin.scheduler.type": "Type",
"admin.scheduler.createdAt": "Created",
"admin.scheduler.updatedAt": "Last Updated",
"common.field.task": "Task",
"admin.scheduler.upcomingNone": "There are no upcoming job for the moment.",
"admin.scheduler.waitUntil": "Start",
"admin.scheduler.attempt": "Attempt",
"admin.scheduler.useWorker": "Execution Mode"
} }

@ -156,6 +156,8 @@ q-layout.admin(view='hHh Lpr lff')
q-item-section(avatar) q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-bot.svg') q-icon(name='img:/_assets/icons/fluent-bot.svg')
q-item-section {{ t('admin.scheduler.title') }} q-item-section {{ t('admin.scheduler.title') }}
q-item-section(side)
status-light(:color='adminStore.info.isSchedulerHealthy ? `positive` : `warning`')
q-item(to='/_admin/security', v-ripple, active-class='bg-primary text-white') q-item(to='/_admin/security', v-ripple, active-class='bg-primary text-white')
q-item-section(avatar) q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-protect.svg') q-icon(name='img:/_assets/icons/fluent-protect.svg')

@ -18,7 +18,9 @@ q-page.admin-terminal
:color='$q.dark.isActive ? `dark-1` : `white`' :color='$q.dark.isActive ? `dark-1` : `white`'
:options=`[ :options=`[
{ label: t('admin.scheduler.scheduled'), value: 'scheduled' }, { label: t('admin.scheduler.scheduled'), value: 'scheduled' },
{ label: t('admin.scheduler.completed'), value: 'completed' } { label: t('admin.scheduler.upcoming'), value: 'upcoming' },
{ label: t('admin.scheduler.completed'), value: 'completed' },
{ label: t('admin.scheduler.failed'), value: 'failed' },
]` ]`
) )
q-separator.q-mr-md(vertical) q-separator.q-mr-md(vertical)
@ -66,18 +68,62 @@ q-page.admin-terminal
size='xs' size='xs'
) )
//- q-icon(name='las la-stopwatch', color='primary', size='sm') //- q-icon(name='las la-stopwatch', color='primary', size='sm')
template(v-slot:body-cell-name='props') template(v-slot:body-cell-task='props')
q-td(:props='props') q-td(:props='props')
strong {{props.value}} strong {{props.value}}
div: small.text-grey {{props.row.id}}
template(v-slot:body-cell-cron='props') template(v-slot:body-cell-cron='props')
q-td(:props='props') q-td(:props='props')
span {{ props.value }} span {{ props.value }}
template(v-else-if='state.displayMode === `upcoming`')
q-card.rounded-borders(
v-if='state.upcomingJobs.length < 1'
flat
:class='$q.dark.isActive ? `bg-dark-5 text-white` : `bg-grey-3 text-dark`'
)
q-card-section.items-center(horizontal)
q-card-section.col-auto.q-pr-none
q-icon(name='las la-info-circle', size='sm')
q-card-section.text-caption {{ t('admin.scheduler.upcomingNone') }}
q-card.shadow-1(v-else)
q-table(
:rows='state.upcomingJobs'
:columns='upcomingJobsHeaders'
row-key='name'
flat
hide-bottom
:rows-per-page-options='[0]'
:loading='state.loading > 0'
)
template(v-slot:body-cell-id='props')
q-td(:props='props')
q-icon(name='las la-chess-knight', color='primary', size='sm')
template(v-slot:body-cell-task='props')
q-td(:props='props')
strong {{props.value}}
div: small.text-grey {{props.row.id}}
template(v-slot:body-cell-waituntil='props')
q-td(:props='props')
span {{ props.value }}
div: small.text-grey {{humanizeDate(props.row.waitUntil)}}
template(v-slot:body-cell-retries='props')
q-td(:props='props')
span #[strong {{props.value + 1}}] #[span.text-grey / {{props.row.maxRetries}}]
template(v-slot:body-cell-useworker='props')
q-td(:props='props')
template(v-if='props.value')
q-icon(name='las la-microchip', color='brown', size='sm')
small.q-ml-xs.text-brown Worker
template(v-else)
q-icon(name='las la-leaf', color='teal', size='sm')
small.q-ml-xs.text-teal In-Process
template(v-slot:body-cell-date='props') template(v-slot:body-cell-date='props')
q-td(:props='props') q-td(:props='props')
i18n-t.text-caption(keypath='admin.scheduler.nextExecutionIn', tag='div') span {{props.value}}
template(#date) div
strong {{ humanizeDate(props.value) }} i18n-t.text-grey(keypath='admin.scheduler.createdBy', tag='small')
small {{props.value}} template(#instance)
strong {{props.row.createdBy}}
template(v-else) template(v-else)
q-card.rounded-borders( q-card.rounded-borders(
v-if='state.jobs.length < 1' v-if='state.jobs.length < 1'
@ -122,8 +168,9 @@ useMeta({
// DATA // DATA
const state = reactive({ const state = reactive({
displayMode: 'scheduled', displayMode: 'upcoming',
scheduledJobs: [], scheduledJobs: [],
upcomingJobs: [],
jobs: [], jobs: [],
loading: 0 loading: 0
}) })
@ -137,10 +184,10 @@ const scheduledJobsHeaders = [
style: 'width: 15px; padding-right: 0;' style: 'width: 15px; padding-right: 0;'
}, },
{ {
label: t('common.field.name'), label: t('common.field.task'),
align: 'left', align: 'left',
field: 'name', field: 'task',
name: 'name', name: 'task',
sortable: true sortable: true
}, },
{ {
@ -148,21 +195,77 @@ const scheduledJobsHeaders = [
align: 'left', align: 'left',
field: 'cron', field: 'cron',
name: 'cron', name: 'cron',
sortable: false sortable: true
},
{
label: t('admin.scheduler.type'),
align: 'left',
field: 'type',
name: 'type',
sortable: true
},
{
label: t('admin.scheduler.createdAt'),
align: 'left',
field: 'createdAt',
name: 'created',
sortable: true,
format: v => DateTime.fromISO(v).toRelative()
},
{
label: t('admin.scheduler.updatedAt'),
align: 'left',
field: 'updatedAt',
name: 'updated',
sortable: true,
format: v => DateTime.fromISO(v).toRelative()
}
]
const upcomingJobsHeaders = [
{
align: 'center',
field: 'id',
name: 'id',
sortable: false,
style: 'width: 15px; padding-right: 0;'
},
{
label: t('common.field.task'),
align: 'left',
field: 'task',
name: 'task',
sortable: true
},
{
label: t('admin.scheduler.waitUntil'),
align: 'left',
field: 'waitUntil',
name: 'waituntil',
sortable: true,
format: v => DateTime.fromISO(v).toRelative()
},
{
label: t('admin.scheduler.attempt'),
align: 'left',
field: 'retries',
name: 'retries',
sortable: true
}, },
{ {
label: t('admin.scheduler.timezone'), label: t('admin.scheduler.useWorker'),
align: 'left', align: 'left',
field: 'timezone', field: 'useWorker',
name: 'timezone', name: 'useworker',
sortable: false sortable: true
}, },
{ {
label: t('admin.scheduler.nextExecution'), label: t('admin.scheduler.createdAt'),
align: 'left', align: 'left',
field: 'nextExecution', field: 'createdAt',
name: 'date', name: 'date',
sortable: false sortable: true,
format: v => DateTime.fromISO(v).toRelative()
} }
] ]
@ -174,33 +277,59 @@ watch(() => state.displayMode, (newValue) => {
// METHODS // METHODS
function humanizeDate (val) {
return DateTime.fromISO(val).toFormat('fff')
}
async function load () { async function load () {
state.loading++ state.loading++
try { try {
if (state.displayMode === 'scheduled') { if (state.displayMode === 'scheduled') {
const resp = await APOLLO_CLIENT.query({ const resp = await APOLLO_CLIENT.query({
query: gql` query: gql`
query getSystemScheduledJobs { query getSystemJobsScheduled {
systemScheduledJobs { systemJobsScheduled {
id id
name task
cron cron
timezone type
nextExecution createdAt
updatedAt
}
}
`,
fetchPolicy: 'network-only'
})
state.scheduledJobs = resp?.data?.systemJobsScheduled
} else if (state.displayMode === 'upcoming') {
const resp = await APOLLO_CLIENT.query({
query: gql`
query getSystemJobsUpcoming {
systemJobsUpcoming {
id
task
useWorker
retries
maxRetries
waitUntil
isScheduled
createdBy
createdAt
updatedAt
} }
} }
`, `,
fetchPolicy: 'network-only' fetchPolicy: 'network-only'
}) })
state.scheduledJobs = resp?.data?.systemScheduledJobs state.upcomingJobs = resp?.data?.systemJobsUpcoming
} else { } else {
const resp = await APOLLO_CLIENT.query({ const resp = await APOLLO_CLIENT.query({
query: gql` query: gql`
query getSystemJobs ( query getSystemJobs (
$type: SystemJobType! $state: SystemJobState!
) { ) {
systemJobs ( systemJobs (
type: $type state: $state
) { ) {
id id
name name
@ -210,7 +339,7 @@ async function load () {
} }
`, `,
variables: { variables: {
type: state.displayMode.toUpperCase() state: state.displayMode.toUpperCase()
}, },
fetchPolicy: 'network-only' fetchPolicy: 'network-only'
}) })
@ -226,10 +355,6 @@ async function load () {
state.loading-- state.loading--
} }
function humanizeDate (val) {
return DateTime.fromISO(val).toRelative()
}
// MOUNTED // MOUNTED
onMounted(() => { onMounted(() => {

@ -16,7 +16,8 @@ export const useAdminStore = defineStore('admin', {
usersTotal: 0, usersTotal: 0,
loginsPastDay: 0, loginsPastDay: 0,
isApiEnabled: false, isApiEnabled: false,
isMailConfigured: false isMailConfigured: false,
isSchedulerHealthy: false
}, },
overlay: null, overlay: null,
overlayOpts: {}, overlayOpts: {},
@ -63,7 +64,8 @@ export const useAdminStore = defineStore('admin', {
usersTotal usersTotal
currentVersion currentVersion
latestVersion latestVersion
mailConfigured isMailConfigured
isSchedulerHealthy
} }
} }
`, `,
@ -74,7 +76,8 @@ export const useAdminStore = defineStore('admin', {
this.info.currentVersion = clone(resp?.data?.systemInfo?.currentVersion ?? 'n/a') this.info.currentVersion = clone(resp?.data?.systemInfo?.currentVersion ?? 'n/a')
this.info.latestVersion = clone(resp?.data?.systemInfo?.latestVersion ?? 'n/a') this.info.latestVersion = clone(resp?.data?.systemInfo?.latestVersion ?? 'n/a')
this.info.isApiEnabled = clone(resp?.data?.apiState ?? false) this.info.isApiEnabled = clone(resp?.data?.apiState ?? false)
this.info.isMailConfigured = clone(resp?.data?.systemInfo?.mailConfigured ?? false) this.info.isMailConfigured = clone(resp?.data?.systemInfo?.isMailConfigured ?? false)
this.info.isSchedulerHealthy = clone(resp?.data?.systemInfo?.isSchedulerHealthy ?? false)
} }
} }
}) })

@ -1706,6 +1706,119 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@volar/language-core@npm:1.0.1":
version: 1.0.1
resolution: "@volar/language-core@npm:1.0.1"
dependencies:
"@volar/source-map": 1.0.1
"@vue/reactivity": ^3.2.38
muggle-string: ^0.1.0
checksum: 82bf72742a0b0063df134ed63b2dc3cab07b2ea4a3d7d8bdcad2e6c81fc2163ce8169996ff97aee30c5c84649b10e3a981d7431a3b404a0b7daa1bf783d7c16b
languageName: node
linkType: hard
"@volar/language-service@npm:1.0.1":
version: 1.0.1
resolution: "@volar/language-service@npm:1.0.1"
dependencies:
"@volar/language-core": 1.0.1
"@volar/shared": 1.0.1
"@volar/source-map": 1.0.1
"@volar/transforms": 1.0.1
"@volar/typescript-faster": 1.0.1
"@vue/reactivity": ^3.2.38
vscode-html-languageservice: ^5.0.1
vscode-json-languageservice: ^5.1.0
vscode-languageserver-protocol: ^3.17.2
vscode-languageserver-textdocument: ^1.0.7
vscode-uri: ^3.0.3
checksum: f25b09cbb67db0f48f4671cd696b61bdb1f4a1b4d841d2a85940a532c90619d57457cbdef3486cfe0702f793a870003720648eab06bca1739ab938a20aa67bfb
languageName: node
linkType: hard
"@volar/pug-language-service@npm:1.0.1":
version: 1.0.1
resolution: "@volar/pug-language-service@npm:1.0.1"
dependencies:
"@volar/language-service": 1.0.1
"@volar/shared": 1.0.1
"@volar/source-map": 1.0.1
"@volar/transforms": 1.0.1
muggle-string: ^0.1.0
pug-lexer: ^5.0.1
pug-parser: ^6.0.0
vscode-languageserver-textdocument: ^1.0.7
vscode-languageserver-types: ^3.17.2
checksum: 8e9545877f7c10feaef0faccda09bbbccff8fe4512c32801d15f9a0098acf313e37b10e83157ddabbc4f7f9649239582f71df4affdcc8422c22dea7a3908b107
languageName: node
linkType: hard
"@volar/shared@npm:1.0.1":
version: 1.0.1
resolution: "@volar/shared@npm:1.0.1"
dependencies:
typesafe-path: ^0.2.1
vscode-languageserver-protocol: ^3.17.2
vscode-languageserver-textdocument: ^1.0.7
vscode-uri: ^3.0.3
checksum: 1a09e4ab140b21607bb0bf6a4ae635a94223dd23c6aae3e7e18e11c1f00b46af68b63e81cb44d2563f8acf008848c24ae865a320ee9a26cce4bd76173e56e0a9
languageName: node
linkType: hard
"@volar/source-map@npm:1.0.1":
version: 1.0.1
resolution: "@volar/source-map@npm:1.0.1"
dependencies:
muggle-string: ^0.1.0
checksum: 333a00484fd667b97fb5f3a339d6a717badf0113fb9352ba042d7ed59e4a5208667362b90d5c4267e7105d187c5f038d67fef71acb9ed52c8a674b149fd3deb8
languageName: node
linkType: hard
"@volar/transforms@npm:1.0.1":
version: 1.0.1
resolution: "@volar/transforms@npm:1.0.1"
dependencies:
"@volar/shared": 1.0.1
vscode-languageserver-types: ^3.17.2
checksum: 6a4f357ee8c862dc7882c02c679378b37b9fb14c9fb5123727671c143310eb78def4c8ff1abe14dee6ac54cd0c6a067fb2a2e9e3b508519bc0fc49b630846ba6
languageName: node
linkType: hard
"@volar/typescript-faster@npm:1.0.1":
version: 1.0.1
resolution: "@volar/typescript-faster@npm:1.0.1"
dependencies:
semver: ^7.3.7
checksum: e67f47c57574b32381956fd0ff6e20b47ce376d14b10886d2ba7b7fe727d3420673e7ec7e8d53f3504aff7ec7562664e8561cbe6f69acf9b47f15176e95f8bf9
languageName: node
linkType: hard
"@volar/vue-language-core@npm:1.0.1":
version: 1.0.1
resolution: "@volar/vue-language-core@npm:1.0.1"
dependencies:
"@volar/language-core": 1.0.1
"@volar/source-map": 1.0.1
"@vue/compiler-dom": ^3.2.38
"@vue/compiler-sfc": ^3.2.38
"@vue/reactivity": ^3.2.38
"@vue/shared": ^3.2.38
minimatch: ^5.1.0
vue-template-compiler: ^2.7.10
checksum: 8c44d5c8ca0f160d0d36fc4bd72bffc86a4f0c786a414b08e0387ec3304daadd33be1b912debd028959318d64c9b857e4abd7efbaae9b4f37aa0e812f6c6444e
languageName: node
linkType: hard
"@volar/vue-language-plugin-pug@npm:1.0.1":
version: 1.0.1
resolution: "@volar/vue-language-plugin-pug@npm:1.0.1"
dependencies:
"@volar/pug-language-service": 1.0.1
"@volar/vue-language-core": 1.0.1
checksum: 08bbac5cdc5be33dffa4d143aceff8ce0ba9b78aad388cb3ed6df3f1a7b61e0221299ea26758b8d90a20d2c9f221bbe431e9f1526fa67ebd607ba069f894a2f3
languageName: node
linkType: hard
"@vue/compiler-core@npm:3.2.37": "@vue/compiler-core@npm:3.2.37":
version: 3.2.37 version: 3.2.37
resolution: "@vue/compiler-core@npm:3.2.37" resolution: "@vue/compiler-core@npm:3.2.37"
@ -1718,6 +1831,18 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@vue/compiler-core@npm:3.2.40":
version: 3.2.40
resolution: "@vue/compiler-core@npm:3.2.40"
dependencies:
"@babel/parser": ^7.16.4
"@vue/shared": 3.2.40
estree-walker: ^2.0.2
source-map: ^0.6.1
checksum: 2683bf13ef93701af1ca4850e887c8d4d67e5583b9c426fc2b08b5512df090bc464955f031cca9f52c11cc6ad49f1ab682011fdf3ba0b6c63b5ae8bea4e68c69
languageName: node
linkType: hard
"@vue/compiler-dom@npm:3.2.37": "@vue/compiler-dom@npm:3.2.37":
version: 3.2.37 version: 3.2.37
resolution: "@vue/compiler-dom@npm:3.2.37" resolution: "@vue/compiler-dom@npm:3.2.37"
@ -1728,6 +1853,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@vue/compiler-dom@npm:3.2.40, @vue/compiler-dom@npm:^3.2.38":
version: 3.2.40
resolution: "@vue/compiler-dom@npm:3.2.40"
dependencies:
"@vue/compiler-core": 3.2.40
"@vue/shared": 3.2.40
checksum: d928a16ebdda9d91a579546d108c9399f8c9a5c9c976196cfefa32f10c0ecb3111233c3291ba05898def85fcfccdc71e3446b977a7cdbc0d47d5d47b0dac75a3
languageName: node
linkType: hard
"@vue/compiler-sfc@npm:3.2.37": "@vue/compiler-sfc@npm:3.2.37":
version: 3.2.37 version: 3.2.37
resolution: "@vue/compiler-sfc@npm:3.2.37" resolution: "@vue/compiler-sfc@npm:3.2.37"
@ -1746,6 +1881,24 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@vue/compiler-sfc@npm:^3.2.38":
version: 3.2.40
resolution: "@vue/compiler-sfc@npm:3.2.40"
dependencies:
"@babel/parser": ^7.16.4
"@vue/compiler-core": 3.2.40
"@vue/compiler-dom": 3.2.40
"@vue/compiler-ssr": 3.2.40
"@vue/reactivity-transform": 3.2.40
"@vue/shared": 3.2.40
estree-walker: ^2.0.2
magic-string: ^0.25.7
postcss: ^8.1.10
source-map: ^0.6.1
checksum: 96cbfd078ad9c5718afced84a1a46dfed87f61bb30ff50ebb929331470d11e672d6a090ad5766ff1e60a5287b7596be31f925af44b6b1bdf69b6f14e938ae7e2
languageName: node
linkType: hard
"@vue/compiler-ssr@npm:3.2.37": "@vue/compiler-ssr@npm:3.2.37":
version: 3.2.37 version: 3.2.37
resolution: "@vue/compiler-ssr@npm:3.2.37" resolution: "@vue/compiler-ssr@npm:3.2.37"
@ -1756,6 +1909,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@vue/compiler-ssr@npm:3.2.40":
version: 3.2.40
resolution: "@vue/compiler-ssr@npm:3.2.40"
dependencies:
"@vue/compiler-dom": 3.2.40
"@vue/shared": 3.2.40
checksum: 026461fcee54cf9968b1e12c32dada6dcde0a322919aa5a2c2e6e13cff7b6b2bdbc06860796895a8deef03ed1f8000e4320878576c498a1f218a62aa3e1c0bf6
languageName: node
linkType: hard
"@vue/devtools-api@npm:^6.1.4": "@vue/devtools-api@npm:^6.1.4":
version: 6.1.4 version: 6.1.4
resolution: "@vue/devtools-api@npm:6.1.4" resolution: "@vue/devtools-api@npm:6.1.4"
@ -1783,6 +1946,19 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@vue/reactivity-transform@npm:3.2.40":
version: 3.2.40
resolution: "@vue/reactivity-transform@npm:3.2.40"
dependencies:
"@babel/parser": ^7.16.4
"@vue/compiler-core": 3.2.40
"@vue/shared": 3.2.40
estree-walker: ^2.0.2
magic-string: ^0.25.7
checksum: b86fc29b52f2460801a3c820370104b734b33cc3a66dbe0ad389a00a62b7a1069121b1ef5dfa50ca3530cbfa98c158743eee0e25af54ca45806f9497757db8c0
languageName: node
linkType: hard
"@vue/reactivity@npm:3.2.37": "@vue/reactivity@npm:3.2.37":
version: 3.2.37 version: 3.2.37
resolution: "@vue/reactivity@npm:3.2.37" resolution: "@vue/reactivity@npm:3.2.37"
@ -1792,6 +1968,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@vue/reactivity@npm:^3.2.38":
version: 3.2.40
resolution: "@vue/reactivity@npm:3.2.40"
dependencies:
"@vue/shared": 3.2.40
checksum: 927d22b424b63a14234810a3b8e4e9127b7238a7cb2fbd749180279048a109348a29fc724fd9d636a6e09b5f4c902f71c789f081d3ab9b4473faedc6a03d7865
languageName: node
linkType: hard
"@vue/runtime-core@npm:3.2.37": "@vue/runtime-core@npm:3.2.37":
version: 3.2.37 version: 3.2.37
resolution: "@vue/runtime-core@npm:3.2.37" resolution: "@vue/runtime-core@npm:3.2.37"
@ -1832,6 +2017,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@vue/shared@npm:3.2.40, @vue/shared@npm:^3.2.38":
version: 3.2.40
resolution: "@vue/shared@npm:3.2.40"
checksum: d91a1e12ffb106a444dcb42c0a54d39f6688f98151dc3b77e8da1e7d3cfd09e1761268d11e7f920f233b43162e727d06f3af4408ef59c53ac2dce9c1d2881511
languageName: node
linkType: hard
"@wry/context@npm:^0.6.0": "@wry/context@npm:^0.6.0":
version: 0.6.1 version: 0.6.1
resolution: "@wry/context@npm:0.6.1" resolution: "@wry/context@npm:0.6.1"
@ -2751,6 +2943,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"de-indent@npm:^1.0.2":
version: 1.0.2
resolution: "de-indent@npm:1.0.2"
checksum: 8deacc0f4a397a4414a0fc4d0034d2b7782e7cb4eaf34943ea47754e08eccf309a0e71fa6f56cc48de429ede999a42d6b4bca761bf91683be0095422dbf24611
languageName: node
linkType: hard
"debug@npm:2.6.9, debug@npm:^2.6.9": "debug@npm:2.6.9, debug@npm:^2.6.9":
version: 2.6.9 version: 2.6.9
resolution: "debug@npm:2.6.9" resolution: "debug@npm:2.6.9"
@ -4797,6 +4996,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"jsonc-parser@npm:^3.2.0":
version: 3.2.0
resolution: "jsonc-parser@npm:3.2.0"
checksum: 946dd9a5f326b745aa326d48a7257e3f4a4b62c5e98ec8e49fa2bdd8d96cef7e6febf1399f5c7016114fd1f68a1c62c6138826d5d90bc650448e3cf0951c53c7
languageName: node
linkType: hard
"jsonfile@npm:^6.0.1": "jsonfile@npm:^6.0.1":
version: 6.1.0 version: 6.1.0
resolution: "jsonfile@npm:6.1.0" resolution: "jsonfile@npm:6.1.0"
@ -5131,6 +5337,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"minimatch@npm:^5.1.0":
version: 5.1.0
resolution: "minimatch@npm:5.1.0"
dependencies:
brace-expansion: ^2.0.1
checksum: 15ce53d31a06361e8b7a629501b5c75491bc2b59712d53e802b1987121d91b433d73fcc5be92974fde66b2b51d8fb28d75a9ae900d249feb792bb1ba2a4f0a90
languageName: node
linkType: hard
"minimist@npm:^1.2.0, minimist@npm:^1.2.6": "minimist@npm:^1.2.0, minimist@npm:^1.2.6":
version: 1.2.6 version: 1.2.6
resolution: "minimist@npm:1.2.6" resolution: "minimist@npm:1.2.6"
@ -5245,6 +5460,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"muggle-string@npm:^0.1.0":
version: 0.1.0
resolution: "muggle-string@npm:0.1.0"
checksum: c892cb53c9e066185913e4d6d0af71df6a2a8d8fd614903d13cd6b260c32ebc7b08ae7190a5faf3f7a25ba01cb9be34844d2a069351c090e4a6013f1eee58a50
languageName: node
linkType: hard
"mute-stream@npm:0.0.8": "mute-stream@npm:0.0.8":
version: 0.0.8 version: 0.0.8
resolution: "mute-stream@npm:0.0.8" resolution: "mute-stream@npm:0.0.8"
@ -6887,6 +7109,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"typesafe-path@npm:^0.2.1":
version: 0.2.1
resolution: "typesafe-path@npm:0.2.1"
checksum: 6f1ed2fb444939e96b018ff6a255de015572f092fbd6243eae3e2e3ebc79684695f9742d418e02c0deb572747170fd9d5da2b89d66cdaf21085bc5b3756fb507
languageName: node
linkType: hard
"uglify-js@npm:^3.5.1": "uglify-js@npm:^3.5.1":
version: 3.15.3 version: 3.15.3
resolution: "uglify-js@npm:3.15.3" resolution: "uglify-js@npm:3.15.3"
@ -7032,6 +7261,7 @@ __metadata:
"@tiptap/starter-kit": 2.0.0-beta.185 "@tiptap/starter-kit": 2.0.0-beta.185
"@tiptap/vue-3": 2.0.0-beta.91 "@tiptap/vue-3": 2.0.0-beta.91
"@types/lodash": 4.14.184 "@types/lodash": 4.14.184
"@volar/vue-language-plugin-pug": 1.0.1
apollo-upload-client: 17.0.0 apollo-upload-client: 17.0.0
browser-fs-access: 0.31.0 browser-fs-access: 0.31.0
browserlist: latest browserlist: latest
@ -7161,6 +7391,76 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"vscode-html-languageservice@npm:^5.0.1":
version: 5.0.2
resolution: "vscode-html-languageservice@npm:5.0.2"
dependencies:
vscode-languageserver-textdocument: ^1.0.7
vscode-languageserver-types: ^3.17.2
vscode-nls: ^5.2.0
vscode-uri: ^3.0.4
checksum: 11fa393f2115c1ecc9ff90502cd17c60268e2908ab0b794275d09d51587c9281d2d04897e2e6bd127d2d2b04bd229ed3211f3966eea3cdc5529999a9a1fcd1fb
languageName: node
linkType: hard
"vscode-json-languageservice@npm:^5.1.0":
version: 5.1.1
resolution: "vscode-json-languageservice@npm:5.1.1"
dependencies:
jsonc-parser: ^3.2.0
vscode-languageserver-textdocument: ^1.0.7
vscode-languageserver-types: ^3.17.2
vscode-nls: ^5.2.0
vscode-uri: ^3.0.6
checksum: ee2910c520572a0342d901b0a94f20c8db3c4cebcfb975529293df6cc7232e406e53c4a80198a6443fe12ba6e6e9513daa2e8f6d52a70fb1c7ce504d3dc44d31
languageName: node
linkType: hard
"vscode-jsonrpc@npm:8.0.2":
version: 8.0.2
resolution: "vscode-jsonrpc@npm:8.0.2"
checksum: 9d055fd4c87ef1093b0eecb5370bfaf3402179b6639149b6d0f7e0bde60cf580091c7e07b0caff868f10f90331b17e7383c087217c077fdd1b5ae7bc23b72f77
languageName: node
linkType: hard
"vscode-languageserver-protocol@npm:^3.17.2":
version: 3.17.2
resolution: "vscode-languageserver-protocol@npm:3.17.2"
dependencies:
vscode-jsonrpc: 8.0.2
vscode-languageserver-types: 3.17.2
checksum: f4a05d3a631af315a32a3700953c2117fa4e5c44bc03764154c6605da9cbbcb50a1b01b46f11b2f6948916d01b4948bebf1a84c135fc73b27fa839c58d0847ab
languageName: node
linkType: hard
"vscode-languageserver-textdocument@npm:^1.0.7":
version: 1.0.7
resolution: "vscode-languageserver-textdocument@npm:1.0.7"
checksum: 6018a8b2c87aeb6441419431909e9161e9659d214814193b029ca2b30d8b097d23538e4930942ef78f1440f52c57a93f7597144736b79ba1abd9f1a53c2ffbc0
languageName: node
linkType: hard
"vscode-languageserver-types@npm:3.17.2, vscode-languageserver-types@npm:^3.17.2":
version: 3.17.2
resolution: "vscode-languageserver-types@npm:3.17.2"
checksum: ef2d862d22f622b64de0f428773d50a5928ec6cdd485960a7564ebe4fd4a3c8bcd956f29eb15bc45a0f353846e62f39f6c764d2ab85ce774b8724411ba84342f
languageName: node
linkType: hard
"vscode-nls@npm:^5.2.0":
version: 5.2.0
resolution: "vscode-nls@npm:5.2.0"
checksum: c9f43c0f85000b3008fc4a3a8fc122e580f4f0402a77186c6c0f3219ca8ac258f4893c7a563d66f097a6da09951d7f5a7e6295d3e21dcbaec707937c9089b5a8
languageName: node
linkType: hard
"vscode-uri@npm:^3.0.3, vscode-uri@npm:^3.0.4, vscode-uri@npm:^3.0.6":
version: 3.0.6
resolution: "vscode-uri@npm:3.0.6"
checksum: 8b6a36553d089309c09f7aa2ca8dae321a1cb7ff5dcab35f0914d5155d3110722bdb6de67dcb727df15fecd83221d11bb4ab1274a9116b9ccc05b86cefe60dfc
languageName: node
linkType: hard
"vue-codemirror@npm:6.0.2": "vue-codemirror@npm:6.0.2":
version: 6.0.2 version: 6.0.2
resolution: "vue-codemirror@npm:6.0.2" resolution: "vue-codemirror@npm:6.0.2"
@ -7235,6 +7535,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"vue-template-compiler@npm:^2.7.10":
version: 2.7.10
resolution: "vue-template-compiler@npm:2.7.10"
dependencies:
de-indent: ^1.0.2
he: ^1.2.0
checksum: 52e4324d93ea5ecf6875c94eae99d3d4197cfb13538b6c2f5020df1776fb277e329325091c41da596b3cf1c7dabd56f50e2a538e2fc3d5ae23438d08471fdc8d
languageName: node
linkType: hard
"vue3-otp-input@npm:0.3.6": "vue3-otp-input@npm:0.3.6":
version: 0.3.6 version: 0.3.6
resolution: "vue3-otp-input@npm:0.3.6" resolution: "vue3-otp-input@npm:0.3.6"

Loading…
Cancel
Save