import Service, { inject as service } from '@ember/service'; import { computed } from '@ember/object'; import { tracked } from '@glimmer/tracking'; import { alias } from '@ember/object/computed'; import PromiseObject from '../utils/classes/promise-object'; import PromiseArray from '../utils/classes/promise-array'; import { namespace } from '../adapters/application'; import jsonWithDefault from '../utils/json-with-default'; import classic from 'ember-classic-decorator'; import { task } from 'ember-concurrency'; @classic export default class SystemService extends Service { @service token; @service store; @computed('activeRegion') get leader() { const token = this.token; return PromiseObject.create({ promise: token .authorizedRequest(`/${namespace}/status/leader`) .then(res => res.json()) .then(rpcAddr => ({ rpcAddr })) .then(leader => { // Dirty self so leader can be used as a dependent key this.notifyPropertyChange('leader.rpcAddr'); return leader; }), }); } @computed get agent() { const token = this.token; return PromiseObject.create({ promise: token .authorizedRawRequest(`/${namespace}/agent/self`) .then(jsonWithDefault({})) .then(agent => { if (agent?.config?.Version) { const { Version, VersionPrerelease, VersionMetadata } = agent.config.Version; agent.version = Version; if (VersionPrerelease) agent.version = `${agent.version}-${VersionPrerelease}`; if (VersionMetadata) agent.version = `${agent.version}+${VersionMetadata}`; } return agent; }), }); } @computed get defaultRegion() { const token = this.token; return PromiseObject.create({ promise: token .authorizedRawRequest(`/${namespace}/agent/members`) .then(jsonWithDefault({})) .then(json => { return { region: json.ServerRegion }; }), }); } @computed get regions() { const token = this.token; return PromiseArray.create({ promise: token.authorizedRawRequest(`/${namespace}/regions`).then(jsonWithDefault([])), }); } @computed('regions.[]') get activeRegion() { const regions = this.regions; const region = window.localStorage.nomadActiveRegion; if (regions.includes(region)) { return region; } return null; } set activeRegion(value) { if (value == null) { window.localStorage.removeItem('nomadActiveRegion'); return; } else { // All localStorage values are strings. Stringify first so // the return value is consistent with what is persisted. const strValue = value + ''; window.localStorage.nomadActiveRegion = strValue; } } @computed('regions.[]') get shouldShowRegions() { return this.get('regions.length') > 1; } @computed('activeRegion', 'defaultRegion.region', 'shouldShowRegions') get shouldIncludeRegion() { return this.shouldShowRegions && this.activeRegion !== this.get('defaultRegion.region'); } @computed('activeRegion') get namespaces() { return PromiseArray.create({ promise: this.store.findAll('namespace').then(namespaces => namespaces.compact()), }); } @computed('namespaces.[]') get shouldShowNamespaces() { const namespaces = this.namespaces.toArray(); return namespaces.length && namespaces.some(namespace => namespace.get('id') !== 'default'); } // The cachedNamespace is set on pages that have a namespaces filter. // It is set so other pages that have a namespaces filter can default to // what the previous namespaces filter page used rather than defaulting // to 'default' or '*'. @tracked cachedNamespace = null; @task(function*() { const emptyLicense = { License: { Features: [] } }; try { return yield this.token .authorizedRawRequest(`/${namespace}/operator/license`) .then(jsonWithDefault(emptyLicense)); } catch (e) { return emptyLicense; } }) fetchLicense; @task(function*() { try { const request = yield this.token.authorizedRequest('/v1/search/fuzzy', { method: 'POST', body: JSON.stringify({ Text: 'feature-detection-query', Context: 'namespaces', }), }); return request.ok; } catch (e) { return false; } }) checkFuzzySearchPresence; @alias('fetchLicense.lastSuccessful.value') license; @alias('checkFuzzySearchPresence.last.value') fuzzySearchEnabled; @computed('license.License.Features.[]') get features() { return this.get('license.License.Features') || []; } }