diff --git a/frontend/lang/messages/en-US.json b/frontend/lang/messages/en-US.json
index 07b577d6f70..dc817971e66 100644
--- a/frontend/lang/messages/en-US.json
+++ b/frontend/lang/messages/en-US.json
@@ -114,6 +114,7 @@
"json": "JSON",
"keyword": "Keyword",
"link-copied": "Link Copied",
+ "loading": "Loading",
"loading-events": "Loading Events",
"loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...",
diff --git a/frontend/pages/admin/site-settings.vue b/frontend/pages/admin/site-settings.vue
index c4d64ad93b9..433674c69da 100644
--- a/frontend/pages/admin/site-settings.vue
+++ b/frontend/pages/admin/site-settings.vue
@@ -130,40 +130,47 @@
@@ -186,6 +193,7 @@ import { useAdminApi, useUserApi } from "~/composables/api";
import { validators } from "~/composables/use-validators";
import { useAsyncKey } from "~/composables/use-utils";
import { CheckAppConfig } from "~/lib/api/types/admin";
+import AppLoader from "~/components/global/AppLoader.vue";
enum DockerVolumeState {
Unknown = "unknown",
@@ -208,294 +216,257 @@ interface CheckApp extends CheckAppConfig {
}
export default defineComponent({
- layout: "admin",
- setup() {
- // ==========================================================
- // Docker Volume Validation
- const docker = reactive({
- loading: false,
- state: DockerVolumeState.Unknown,
- });
-
- async function dockerValidate() {
- docker.loading = true;
-
- // Do API Check
- const { data } = await adminApi.about.checkDocker();
- if (data == null) {
- docker.state = DockerVolumeState.Error;
- return;
- }
-
- // Get File Contents
- const { data: fileContents } = await adminApi.about.getDockerValidateFileContents();
-
- if (data.text === fileContents) {
- docker.state = DockerVolumeState.Success;
- } else {
- docker.state = DockerVolumeState.Error;
- }
-
- docker.loading = false;
- }
-
- const state = reactive({
- loading: false,
- address: "",
- success: false,
- error: "",
- tested: false,
- });
-
- const appConfig = ref({
- emailReady: true,
- baseUrlSet: true,
- isSiteSecure: true,
- isUpToDate: false,
- ldapReady: false,
- });
-
- function isLocalHostOrHttps() {
- return window.location.hostname === "localhost" || window.location.protocol === "https:";
- }
-
- const api = useUserApi();
- const adminApi = useAdminApi();
-
- onMounted(async () => {
- const { data } = await adminApi.about.checkApp();
-
- if (data) {
- appConfig.value = { ...data, isSiteSecure: false };
- }
-
- appConfig.value.isSiteSecure = isLocalHostOrHttps();
- });
-
- const simpleChecks = computed(() => {
- const goodIcon = $globals.icons.checkboxMarkedCircle;
- const badIcon = $globals.icons.alert;
- const warningIcon = $globals.icons.alertCircle;
-
- const goodColor = "success";
- const badColor = "error";
- const warningColor = "warning";
-
-
- const data: SimpleCheck[] = [
- {
- id: "application-version",
- text: i18n.t("settings.application-version"),
- status: appConfig.value.isUpToDate,
- errorText: i18n.t("settings.application-version-error-text", [rawAppInfo.value.version, rawAppInfo.value.versionLatest]),
- successText: i18n.t("settings.mealie-is-up-to-date"),
- color: appConfig.value.isUpToDate ? goodColor : warningColor,
- icon: appConfig.value.isUpToDate ? goodIcon : warningIcon,
- },
- {
- id: "secure-site",
- text: i18n.t("settings.secure-site"),
- status: appConfig.value.isSiteSecure,
- errorText: i18n.t("settings.secure-site-error-text"),
- successText: i18n.t("settings.secure-site-success-text"),
- color: appConfig.value.isSiteSecure ? goodColor : badColor,
- icon: appConfig.value.isSiteSecure ? goodIcon : badIcon,
- },
- {
- id: "server-side-base-url",
- text: i18n.t("settings.server-side-base-url"),
- status: appConfig.value.baseUrlSet,
- errorText:
- i18n.t("settings.server-side-base-url-error-text"),
- successText: i18n.t("settings.server-side-base-url-success-text"),
- color: appConfig.value.baseUrlSet ? goodColor : badColor,
- icon: appConfig.value.baseUrlSet ? goodIcon : badIcon,
- },
- {
- id: "ldap-ready",
- text: i18n.t("settings.ldap-ready"),
- status: appConfig.value.ldapReady,
- errorText:
- i18n.t("settings.ldap-ready-error-text"),
- successText: i18n.t("settings.ldap-ready-success-text"),
- color: appConfig.value.ldapReady ? goodColor : warningColor,
- icon: appConfig.value.ldapReady ? goodIcon : warningIcon,
- },
- ];
-
- return data;
- });
-
- async function testEmail() {
- state.loading = true;
- state.tested = false;
- const { data } = await api.email.test({ email: state.address });
-
- if (data) {
- if (data.success) {
- state.success = true;
- } else {
- state.error = data.error ?? "";
- state.success = false;
+ layout: "admin",
+ setup() {
+ // ==========================================================
+ // Docker Volume Validation
+ const docker = reactive({
+ loading: false,
+ state: DockerVolumeState.Unknown,
+ });
+ async function dockerValidate() {
+ docker.loading = true;
+ // Do API Check
+ const { data } = await adminApi.about.checkDocker();
+ if (data == null) {
+ docker.state = DockerVolumeState.Error;
+ return;
+ }
+ // Get File Contents
+ const { data: fileContents } = await adminApi.about.getDockerValidateFileContents();
+ if (data.text === fileContents) {
+ docker.state = DockerVolumeState.Success;
+ }
+ else {
+ docker.state = DockerVolumeState.Error;
+ }
+ docker.loading = false;
}
- }
- state.loading = false;
- state.tested = true;
- }
-
- const validEmail = computed(() => {
- if (state.address === "") {
- return false;
- }
- const valid = validators.email(state.address);
-
- // Explicit bool check because validators.email sometimes returns a string
- if (valid === true) {
- return true;
- }
- return false;
- });
-
- // ============================================================
- // General About Info
-
- const { $globals, i18n } = useContext();
-
- const rawAppInfo = ref({
- version: "null",
- versionLatest: "null",
- });
-
- function getAppInfo() {
- const statistics = useAsync(async () => {
- const { data } = await adminApi.about.about();
-
- if (data) {
- rawAppInfo.value.version = data.version;
- rawAppInfo.value.versionLatest = data.versionLatest;
-
- const prettyInfo = [
- {
- name: i18n.t("about.version"),
- icon: $globals.icons.information,
- value: data.version,
- },
- {
- slot: "build",
- name: i18n.t("settings.build"),
- icon: $globals.icons.information,
- value: data.buildId,
- },
- {
- name: i18n.t("about.application-mode"),
- icon: $globals.icons.devTo,
- value: data.production ? i18n.t("about.production") : i18n.t("about.development"),
- },
- {
- name: i18n.t("about.demo-status"),
- icon: $globals.icons.testTube,
- value: data.demoStatus ? i18n.t("about.demo") : i18n.t("about.not-demo"),
- },
- {
- name: i18n.t("about.api-port"),
- icon: $globals.icons.api,
- value: data.apiPort,
- },
- {
- name: i18n.t("about.api-docs"),
- icon: $globals.icons.file,
- value: data.apiDocs ? i18n.t("general.enabled") : i18n.t("general.disabled"),
- },
- {
- name: i18n.t("about.database-type"),
- icon: $globals.icons.database,
- value: data.dbType,
- },
- {
- name: i18n.t("about.database-url"),
- icon: $globals.icons.database,
- value: data.dbUrl,
- },
- {
- name: i18n.t("about.default-group"),
- icon: $globals.icons.group,
- value: data.defaultGroup,
- },
- {
- slot: "recipe-scraper",
- name: i18n.t("settings.recipe-scraper-version"),
- icon: $globals.icons.primary,
- value: data.recipeScraperVersion,
- },
- ];
-
- return prettyInfo;
+ const state = reactive({
+ loading: false,
+ address: "",
+ success: false,
+ error: "",
+ tested: false,
+ });
+ const appConfig = ref({
+ emailReady: true,
+ baseUrlSet: true,
+ isSiteSecure: true,
+ isUpToDate: false,
+ ldapReady: false,
+ });
+ function isLocalHostOrHttps() {
+ return window.location.hostname === "localhost" || window.location.protocol === "https:";
}
-
- return data;
- }, useAsyncKey());
-
- return statistics;
- }
-
- const appInfo = getAppInfo();
-
- const bugReportDialog = ref(false);
-
- const bugReportText = computed(() => {
- const ignore = {
- [i18n.tc("about.database-url")]: true,
- [i18n.tc("about.default-group")]: true,
- };
- let text = "**Details**\n";
-
- appInfo.value?.forEach((item) => {
- if (ignore[item.name as string]) {
- return;
+ const api = useUserApi();
+ const adminApi = useAdminApi();
+ onMounted(async () => {
+ const { data } = await adminApi.about.checkApp();
+ if (data) {
+ appConfig.value = { ...data, isSiteSecure: false };
+ }
+ appConfig.value.isSiteSecure = isLocalHostOrHttps();
+ });
+ const simpleChecks = computed(() => {
+ const goodIcon = $globals.icons.checkboxMarkedCircle;
+ const badIcon = $globals.icons.alert;
+ const warningIcon = $globals.icons.alertCircle;
+ const goodColor = "success";
+ const badColor = "error";
+ const warningColor = "warning";
+ const data: SimpleCheck[] = [
+ {
+ id: "application-version",
+ text: i18n.t("settings.application-version"),
+ status: appConfig.value.isUpToDate,
+ errorText: i18n.t("settings.application-version-error-text", [rawAppInfo.value.version, rawAppInfo.value.versionLatest]),
+ successText: i18n.t("settings.mealie-is-up-to-date"),
+ color: appConfig.value.isUpToDate ? goodColor : warningColor,
+ icon: appConfig.value.isUpToDate ? goodIcon : warningIcon,
+ },
+ {
+ id: "secure-site",
+ text: i18n.t("settings.secure-site"),
+ status: appConfig.value.isSiteSecure,
+ errorText: i18n.t("settings.secure-site-error-text"),
+ successText: i18n.t("settings.secure-site-success-text"),
+ color: appConfig.value.isSiteSecure ? goodColor : badColor,
+ icon: appConfig.value.isSiteSecure ? goodIcon : badIcon,
+ },
+ {
+ id: "server-side-base-url",
+ text: i18n.t("settings.server-side-base-url"),
+ status: appConfig.value.baseUrlSet,
+ errorText: i18n.t("settings.server-side-base-url-error-text"),
+ successText: i18n.t("settings.server-side-base-url-success-text"),
+ color: appConfig.value.baseUrlSet ? goodColor : badColor,
+ icon: appConfig.value.baseUrlSet ? goodIcon : badIcon,
+ },
+ {
+ id: "ldap-ready",
+ text: i18n.t("settings.ldap-ready"),
+ status: appConfig.value.ldapReady,
+ errorText: i18n.t("settings.ldap-ready-error-text"),
+ successText: i18n.t("settings.ldap-ready-success-text"),
+ color: appConfig.value.ldapReady ? goodColor : warningColor,
+ icon: appConfig.value.ldapReady ? goodIcon : warningIcon,
+ },
+ ];
+ return data;
+ });
+ async function testEmail() {
+ state.loading = true;
+ state.tested = false;
+ const { data } = await api.email.test({ email: state.address });
+ if (data) {
+ if (data.success) {
+ state.success = true;
+ }
+ else {
+ state.error = data.error ?? "";
+ state.success = false;
+ }
+ }
+ state.loading = false;
+ state.tested = true;
}
- text += `${item.name as string}: ${item.value as string}\n`;
- });
-
- const ignoreChecks: { [key: string]: boolean } = {
- "application-version": true,
- };
-
- text += "\n**Checks**\n";
-
- simpleChecks.value.forEach((item) => {
- if (ignoreChecks[item.id]) {
- return;
+ const validEmail = computed(() => {
+ if (state.address === "") {
+ return false;
+ }
+ const valid = validators.email(state.address);
+ // Explicit bool check because validators.email sometimes returns a string
+ if (valid === true) {
+ return true;
+ }
+ return false;
+ });
+ // ============================================================
+ // General About Info
+ const { $globals, i18n } = useContext();
+ const rawAppInfo = ref({
+ version: "null",
+ versionLatest: "null",
+ });
+ function getAppInfo() {
+ const statistics = useAsync(async () => {
+ const { data } = await adminApi.about.about();
+ if (data) {
+ rawAppInfo.value.version = data.version;
+ rawAppInfo.value.versionLatest = data.versionLatest;
+ const prettyInfo = [
+ {
+ name: i18n.t("about.version"),
+ icon: $globals.icons.information,
+ value: data.version,
+ },
+ {
+ slot: "build",
+ name: i18n.t("settings.build"),
+ icon: $globals.icons.information,
+ value: data.buildId,
+ },
+ {
+ name: i18n.t("about.application-mode"),
+ icon: $globals.icons.devTo,
+ value: data.production ? i18n.t("about.production") : i18n.t("about.development"),
+ },
+ {
+ name: i18n.t("about.demo-status"),
+ icon: $globals.icons.testTube,
+ value: data.demoStatus ? i18n.t("about.demo") : i18n.t("about.not-demo"),
+ },
+ {
+ name: i18n.t("about.api-port"),
+ icon: $globals.icons.api,
+ value: data.apiPort,
+ },
+ {
+ name: i18n.t("about.api-docs"),
+ icon: $globals.icons.file,
+ value: data.apiDocs ? i18n.t("general.enabled") : i18n.t("general.disabled"),
+ },
+ {
+ name: i18n.t("about.database-type"),
+ icon: $globals.icons.database,
+ value: data.dbType,
+ },
+ {
+ name: i18n.t("about.database-url"),
+ icon: $globals.icons.database,
+ value: data.dbUrl,
+ },
+ {
+ name: i18n.t("about.default-group"),
+ icon: $globals.icons.group,
+ value: data.defaultGroup,
+ },
+ {
+ slot: "recipe-scraper",
+ name: i18n.t("settings.recipe-scraper-version"),
+ icon: $globals.icons.primary,
+ value: data.recipeScraperVersion,
+ },
+ ];
+ return prettyInfo;
+ }
+ return data;
+ }, useAsyncKey());
+ return statistics;
}
- const status = item.status ? i18n.tc("general.yes") : i18n.tc("general.no");
- text += `${item.text.toString()}: ${status}\n`;
- });
-
- text += `${i18n.tc("settings.email-configured")}: ${appConfig.value.emailReady ? i18n.tc("general.yes") : i18n.tc("general.no")}\n`;
- text += `${i18n.tc("settings.docker-volume")}: ${docker.state}`;
-
- return text;
- });
-
- return {
- bugReportDialog,
- bugReportText,
- DockerVolumeState,
- docker,
- dockerValidate,
- simpleChecks,
- appConfig,
- validEmail,
- validators,
- ...toRefs(state),
- testEmail,
- appInfo,
- };
- },
- head() {
- return {
- title: this.$t("settings.site-settings") as string,
- };
- },
+ const appInfo = getAppInfo();
+ const bugReportDialog = ref(false);
+ const bugReportText = computed(() => {
+ const ignore = {
+ [i18n.tc("about.database-url")]: true,
+ [i18n.tc("about.default-group")]: true,
+ };
+ let text = "**Details**\n";
+ appInfo.value?.forEach((item) => {
+ if (ignore[item.name as string]) {
+ return;
+ }
+ text += `${item.name as string}: ${item.value as string}\n`;
+ });
+ const ignoreChecks: {
+ [key: string]: boolean;
+ } = {
+ "application-version": true,
+ };
+ text += "\n**Checks**\n";
+ simpleChecks.value.forEach((item) => {
+ if (ignoreChecks[item.id]) {
+ return;
+ }
+ const status = item.status ? i18n.tc("general.yes") : i18n.tc("general.no");
+ text += `${item.text.toString()}: ${status}\n`;
+ });
+ text += `${i18n.tc("settings.email-configured")}: ${appConfig.value.emailReady ? i18n.tc("general.yes") : i18n.tc("general.no")}\n`;
+ text += `${i18n.tc("settings.docker-volume")}: ${docker.state}`;
+ return text;
+ });
+ return {
+ bugReportDialog,
+ bugReportText,
+ DockerVolumeState,
+ docker,
+ dockerValidate,
+ simpleChecks,
+ appConfig,
+ validEmail,
+ validators,
+ ...toRefs(state),
+ testEmail,
+ appInfo,
+ };
+ },
+ head() {
+ return {
+ title: this.$t("settings.site-settings") as string,
+ };
+ },
+ components: { AppLoader }
});