mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-07-09 03:04:54 -04:00
More localization (#358)
* Translate missing items on About page * Localize import summary dialog * Make site menu translation reactive * Localize import options * Include semi colon in string * Move API texts to frontend + better status codes * Provide feedback to user when no meal is planned * Fix API tests after latest rework * Add warning for API changes in changelog * Refactor API texts handling * Refactor API texts handling #2 * Better API feedback * Rearrange strings hierarchy * Add messages upon recipe updated * Fix 'recipe effected' typo * Remove snackbar usage in backend * Translate toolbox * Provide feedback for tags CRUD * Fix messed up merge * Translate sign-up form * Better feedback for sign-up CRUD * Refactor log-in API texts handling * No error message when user is not authenticated * Remove unimportant console log
This commit is contained in:
parent
861020ffe0
commit
1e5edc7434
@ -17,6 +17,8 @@
|
|||||||
- Fixes #281 - Slow Handling of Large Sets of Recipes
|
- Fixes #281 - Slow Handling of Large Sets of Recipes
|
||||||
|
|
||||||
## Features and Improvements
|
## Features and Improvements
|
||||||
|
- 'Dinner this week' shows a warning when no meal is planned yet
|
||||||
|
- 'Dinner today' shows a warning when no meal is planned yet
|
||||||
|
|
||||||
### General
|
### General
|
||||||
- New Toolbox Page!
|
- New Toolbox Page!
|
||||||
@ -38,3 +40,6 @@
|
|||||||
### Behind the Scenes
|
### Behind the Scenes
|
||||||
- Unified Sidebar Components
|
- Unified Sidebar Components
|
||||||
- Refactor UI components to fit Vue best practices (WIP)
|
- Refactor UI components to fit Vue best practices (WIP)
|
||||||
|
- The API returns more consistent status codes
|
||||||
|
- The API returns error code instead of error text when appropriate
|
||||||
|
- ⚠️ May cause side-effects if you were directly consuming the API
|
@ -1,75 +1,57 @@
|
|||||||
const baseURL = "/api/";
|
const baseURL = "/api/";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import utils from "@/utils";
|
|
||||||
import { store } from "../store";
|
import { store } from "../store";
|
||||||
|
import utils from "@/utils";
|
||||||
|
|
||||||
axios.defaults.headers.common[
|
axios.defaults.headers.common[
|
||||||
"Authorization"
|
"Authorization"
|
||||||
] = `Bearer ${store.getters.getToken}`;
|
] = `Bearer ${store.getters.getToken}`;
|
||||||
|
|
||||||
function processResponse(response) {
|
function handleError(error, getText) {
|
||||||
try {
|
if(getText) {
|
||||||
utils.notify.show(response.data.snackbar.text, response.data.snackbar.type);
|
utils.notify.error(getText(error.response));
|
||||||
} catch (err) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function handleResponse(response, getText) {
|
||||||
|
if(response && getText) {
|
||||||
|
const successText = getText(response);
|
||||||
|
utils.notify.success(successText);
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
function defaultErrorText(response) {
|
||||||
|
return response.statusText;
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultSuccessText(response) {
|
||||||
|
return response.statusText;
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiReq = {
|
const apiReq = {
|
||||||
post: async function(url, data) {
|
post: async function(url, data, getErrorText = defaultErrorText, getSuccessText) {
|
||||||
let response = await axios.post(url, data).catch(function(error) {
|
const response = await axios.post(url, data).catch(function(error) { handleError(error, getErrorText) });
|
||||||
if (error.response) {
|
return handleResponse(response, getSuccessText);
|
||||||
processResponse(error.response);
|
|
||||||
return error.response;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
processResponse(response);
|
|
||||||
return response;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
put: async function(url, data) {
|
put: async function(url, data, getErrorText = defaultErrorText, getSuccessText) {
|
||||||
let response = await axios.put(url, data).catch(function(error) {
|
const response = await axios.put(url, data).catch(function(error) { handleError(error, getErrorText) });
|
||||||
if (error.response) {
|
return handleResponse(response, getSuccessText);
|
||||||
processResponse(error.response);
|
|
||||||
return response;
|
|
||||||
} else return;
|
|
||||||
});
|
|
||||||
processResponse(response);
|
|
||||||
return response;
|
|
||||||
},
|
|
||||||
patch: async function(url, data) {
|
|
||||||
let response = await axios.patch(url, data).catch(function(error) {
|
|
||||||
if (error.response) {
|
|
||||||
processResponse(error.response);
|
|
||||||
return response;
|
|
||||||
} else return;
|
|
||||||
});
|
|
||||||
processResponse(response);
|
|
||||||
return response;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
get: async function(url, data) {
|
patch: async function(url, data, getErrorText = defaultErrorText, getSuccessText) {
|
||||||
let response = await axios.get(url, data).catch(function(error) {
|
const response = await axios.patch(url, data).catch(function(error) { handleError(error, getErrorText) });
|
||||||
if (error.response) {
|
return handleResponse(response, getSuccessText);
|
||||||
processResponse(error.response);
|
|
||||||
return response;
|
|
||||||
} else return;
|
|
||||||
});
|
|
||||||
processResponse(response);
|
|
||||||
return response;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
delete: async function(url, data) {
|
get: function(url, data, getErrorText = defaultErrorText) {
|
||||||
let response = await axios.delete(url, data).catch(function(error) {
|
return axios.get(url, data).catch(function(error) { handleError(error, getErrorText) });
|
||||||
if (error.response) {
|
},
|
||||||
processResponse(error.response);
|
|
||||||
return response;
|
delete: async function(url, data, getErrorText = defaultErrorText, getSuccessText = defaultSuccessText ) {
|
||||||
}
|
const response = await axios.delete(url, data).catch( function(error) { handleError(error, getErrorText) } );
|
||||||
});
|
return handleResponse(response, getSuccessText);
|
||||||
processResponse(response);
|
|
||||||
return response;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async download(url) {
|
async download(url) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { baseURL } from "./api-utils";
|
import { baseURL } from "./api-utils";
|
||||||
import { apiReq } from "./api-utils";
|
import { apiReq } from "./api-utils";
|
||||||
import { store } from "@/store";
|
import { store } from "@/store";
|
||||||
|
import i18n from '@/i18n.js';
|
||||||
|
|
||||||
const backupBase = baseURL + "backups/";
|
const backupBase = baseURL + "backups/";
|
||||||
|
|
||||||
@ -40,7 +41,12 @@ export const backupAPI = {
|
|||||||
* @param {string} fileName
|
* @param {string} fileName
|
||||||
*/
|
*/
|
||||||
async delete(fileName) {
|
async delete(fileName) {
|
||||||
await apiReq.delete(backupURLs.deleteBackup(fileName));
|
return apiReq.delete(
|
||||||
|
backupURLs.deleteBackup(fileName),
|
||||||
|
null,
|
||||||
|
function() { return i18n.t('settings.backup.unable-to-delete-backup'); },
|
||||||
|
function() { return i18n.t('settings.backup.backup-deleted'); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Creates a backup on the serve given a set of options
|
* Creates a backup on the serve given a set of options
|
||||||
@ -48,8 +54,12 @@ export const backupAPI = {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async create(options) {
|
async create(options) {
|
||||||
let response = apiReq.post(backupURLs.createBackup, options);
|
return apiReq.post(
|
||||||
return response;
|
backupURLs.createBackup,
|
||||||
|
options,
|
||||||
|
function() { return i18n.t('settings.backup.error-creating-backup-see-log-file'); },
|
||||||
|
function(response) { return i18n.t('settings.backup.backup-created-at-response-export_path', {path: response.data.export_path}); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Downloads a file from the server. I don't actually think this is used?
|
* Downloads a file from the server. I don't actually think this is used?
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { baseURL } from "./api-utils";
|
import { baseURL } from "./api-utils";
|
||||||
import { apiReq } from "./api-utils";
|
import { apiReq } from "./api-utils";
|
||||||
import { store } from "@/store";
|
import { store } from "@/store";
|
||||||
|
import i18n from '@/i18n.js';
|
||||||
|
|
||||||
const prefix = baseURL + "categories";
|
const prefix = baseURL + "categories";
|
||||||
|
|
||||||
@ -22,29 +23,44 @@ export const categoryAPI = {
|
|||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
async create(name) {
|
async create(name) {
|
||||||
let response = await apiReq.post(categoryURLs.getAll, { name: name });
|
const response = await apiReq.post(
|
||||||
|
categoryURLs.getAll,
|
||||||
|
{ name: name },
|
||||||
|
function() { return i18n.t('category.category-creation-failed'); },
|
||||||
|
function() { return i18n.t('category.category-created'); }
|
||||||
|
);
|
||||||
|
if(response) {
|
||||||
store.dispatch("requestCategories");
|
store.dispatch("requestCategories");
|
||||||
return response.data;
|
return response.data;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async getRecipesInCategory(category) {
|
async getRecipesInCategory(category) {
|
||||||
let response = await apiReq.get(categoryURLs.getCategory(category));
|
let response = await apiReq.get(categoryURLs.getCategory(category));
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
async update(name, newName, overrideRequest = false) {
|
async update(name, newName, overrideRequest = false) {
|
||||||
let response = await apiReq.put(categoryURLs.updateCategory(name), {
|
const response = await apiReq.put(
|
||||||
name: newName,
|
categoryURLs.updateCategory(name),
|
||||||
});
|
{ name: newName },
|
||||||
if (!overrideRequest) {
|
function() { return i18n.t('category.category-update-failed'); },
|
||||||
|
function() { return i18n.t('category.category-updated'); }
|
||||||
|
);
|
||||||
|
if (response && !overrideRequest) {
|
||||||
store.dispatch("requestCategories");
|
store.dispatch("requestCategories");
|
||||||
}
|
|
||||||
return response.data;
|
return response.data;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async delete(category, overrideRequest = false) {
|
async delete(category, overrideRequest = false) {
|
||||||
let response = await apiReq.delete(categoryURLs.deleteCategory(category));
|
const response = await apiReq.delete(
|
||||||
if (!overrideRequest) {
|
categoryURLs.deleteCategory(category),
|
||||||
|
null,
|
||||||
|
function() { return i18n.t('category.category-deletion-failed'); },
|
||||||
|
function() { return i18n.t('category.category-deleted'); }
|
||||||
|
);
|
||||||
|
if (response && !overrideRequest) {
|
||||||
store.dispatch("requestCategories");
|
store.dispatch("requestCategories");
|
||||||
}
|
}
|
||||||
return response.data;
|
return response;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -68,28 +84,48 @@ export const tagAPI = {
|
|||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
async create(name) {
|
async create(name) {
|
||||||
let response = await apiReq.post(tagURLs.getAll, { name: name });
|
const response = await apiReq.post(
|
||||||
|
tagURLs.getAll,
|
||||||
|
{ name: name },
|
||||||
|
function() { return i18n.t('tag.tag-creation-failed'); },
|
||||||
|
function() { return i18n.t('tag.tag-created'); }
|
||||||
|
);
|
||||||
|
if(response) {
|
||||||
store.dispatch("requestTags");
|
store.dispatch("requestTags");
|
||||||
return response.data;
|
return response.data;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async getRecipesInTag(tag) {
|
async getRecipesInTag(tag) {
|
||||||
let response = await apiReq.get(tagURLs.getTag(tag));
|
let response = await apiReq.get(tagURLs.getTag(tag));
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
async update(name, newName, overrideRequest = false) {
|
async update(name, newName, overrideRequest = false) {
|
||||||
let response = await apiReq.put(tagURLs.updateTag(name), { name: newName });
|
const response = await apiReq.put(
|
||||||
|
tagURLs.updateTag(name),
|
||||||
|
{ name: newName },
|
||||||
|
function() { return i18n.t('tag.tag-update-failed'); },
|
||||||
|
function() { return i18n.t('tag.tag-updated'); }
|
||||||
|
);
|
||||||
|
|
||||||
|
if(response) {
|
||||||
if (!overrideRequest) {
|
if (!overrideRequest) {
|
||||||
store.dispatch("requestTags");
|
store.dispatch("requestTags");
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.data;
|
return response.data;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async delete(tag, overrideRequest = false) {
|
async delete(tag, overrideRequest = false) {
|
||||||
let response = await apiReq.delete(tagURLs.deleteTag(tag));
|
const response = await apiReq.delete(
|
||||||
|
tagURLs.deleteTag(tag),
|
||||||
|
null,
|
||||||
|
function() { return i18n.t('tag.tag-deletion-failed'); },
|
||||||
|
function() { return i18n.t('tag.tag-deleted'); }
|
||||||
|
);
|
||||||
|
if(response) {
|
||||||
if (!overrideRequest) {
|
if (!overrideRequest) {
|
||||||
store.dispatch("requestTags");
|
store.dispatch("requestTags");
|
||||||
}
|
}
|
||||||
return response.data;
|
return response.data;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { baseURL } from "./api-utils";
|
import { baseURL } from "./api-utils";
|
||||||
import { apiReq } from "./api-utils";
|
import { apiReq } from "./api-utils";
|
||||||
|
import i18n from '@/i18n.js';
|
||||||
const groupPrefix = baseURL + "groups";
|
const groupPrefix = baseURL + "groups";
|
||||||
|
|
||||||
const groupsURLs = {
|
const groupsURLs = {
|
||||||
@ -10,25 +11,58 @@ const groupsURLs = {
|
|||||||
update: id => `${groupPrefix}/${id}`,
|
update: id => `${groupPrefix}/${id}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function deleteErrorText(response) {
|
||||||
|
switch(response.data.detail) {
|
||||||
|
case 'GROUP_WITH_USERS':
|
||||||
|
return i18n.t('group.cannot-delete-group-with-users');
|
||||||
|
|
||||||
|
case 'GROUP_NOT_FOUND':
|
||||||
|
return i18n.t('group.group-not-found');
|
||||||
|
|
||||||
|
case 'DEFAULT_GROUP':
|
||||||
|
return i18n.t('group.cannot-delete-default-group');
|
||||||
|
|
||||||
|
default:
|
||||||
|
return i18n.t('group.group-deletion-failed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const groupAPI = {
|
export const groupAPI = {
|
||||||
async allGroups() {
|
async allGroups() {
|
||||||
let response = await apiReq.get(groupsURLs.groups);
|
let response = await apiReq.get(groupsURLs.groups);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
async create(name) {
|
create(name) {
|
||||||
let response = await apiReq.post(groupsURLs.create, { name: name });
|
return apiReq.post(
|
||||||
return response.data;
|
groupsURLs.create,
|
||||||
|
{ name: name },
|
||||||
|
function() { return i18n.t('group.user-group-creation-failed'); },
|
||||||
|
function() { return i18n.t('group.user-group-created'); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
async delete(id) {
|
delete(id) {
|
||||||
let response = await apiReq.delete(groupsURLs.delete(id));
|
return apiReq.delete(
|
||||||
return response.data;
|
groupsURLs.delete(id),
|
||||||
|
null,
|
||||||
|
deleteErrorText,
|
||||||
|
function() { return i18n.t('group.group-deleted'); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
async current() {
|
async current() {
|
||||||
let response = await apiReq.get(groupsURLs.current);
|
const response = await apiReq.get(
|
||||||
|
groupsURLs.current,
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
if(response) {
|
||||||
return response.data;
|
return response.data;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async update(data) {
|
update(data) {
|
||||||
let response = await apiReq.put(groupsURLs.update(data.id), data);
|
return apiReq.put(
|
||||||
return response.data;
|
groupsURLs.update(data.id),
|
||||||
|
data,
|
||||||
|
function() { return i18n.t('group.error-updating-group'); },
|
||||||
|
function() { return i18n.t('settings.group-settings-updated'); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { baseURL } from "./api-utils";
|
import { baseURL } from "./api-utils";
|
||||||
import { apiReq } from "./api-utils";
|
import { apiReq } from "./api-utils";
|
||||||
|
import i18n from '@/i18n.js';
|
||||||
|
|
||||||
const prefix = baseURL + "meal-plans/";
|
const prefix = baseURL + "meal-plans/";
|
||||||
|
|
||||||
@ -15,9 +16,13 @@ const mealPlanURLs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const mealplanAPI = {
|
export const mealplanAPI = {
|
||||||
async create(postBody) {
|
create(postBody) {
|
||||||
let response = await apiReq.post(mealPlanURLs.create, postBody);
|
return apiReq.post(
|
||||||
return response;
|
mealPlanURLs.create,
|
||||||
|
postBody,
|
||||||
|
function() { return i18n.t('meal-plan.mealplan-creation-failed')},
|
||||||
|
function() { return i18n.t('meal-plan.mealplan-created'); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
async all() {
|
async all() {
|
||||||
@ -35,14 +40,21 @@ export const mealplanAPI = {
|
|||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
|
|
||||||
async delete(id) {
|
delete(id) {
|
||||||
let response = await apiReq.delete(mealPlanURLs.delete(id));
|
return apiReq.delete(mealPlanURLs.delete(id),
|
||||||
return response;
|
null,
|
||||||
|
function() { return i18n.t('meal-plan.mealplan-deletion-failed'); },
|
||||||
|
function() { return i18n.t('meal-plan.mealplan-deleted'); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
async update(id, body) {
|
update(id, body) {
|
||||||
let response = await apiReq.put(mealPlanURLs.update(id), body);
|
return apiReq.put(
|
||||||
return response;
|
mealPlanURLs.update(id),
|
||||||
|
body,
|
||||||
|
function() { return i18n.t('meal-plan.mealplan-update-failed'); },
|
||||||
|
function() { return i18n.t('meal-plan.mealplan-updated'); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
async shoppingList(id) {
|
async shoppingList(id) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { baseURL } from "./api-utils";
|
import { baseURL } from "./api-utils";
|
||||||
import { apiReq } from "./api-utils";
|
import { apiReq } from "./api-utils";
|
||||||
import { store } from "../store";
|
import { store } from "../store";
|
||||||
|
import i18n from '@/i18n.js';
|
||||||
|
|
||||||
const migrationBase = baseURL + "migrations";
|
const migrationBase = baseURL + "migrations";
|
||||||
|
|
||||||
@ -17,8 +18,13 @@ export const migrationAPI = {
|
|||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
async delete(folder, file) {
|
async delete(folder, file) {
|
||||||
let response = await apiReq.delete(migrationURLs.delete(folder, file));
|
const response = await apiReq.delete(
|
||||||
return response.data;
|
migrationURLs.delete(folder, file),
|
||||||
|
null,
|
||||||
|
function() { return i18n.t('general.file-folder-not-found'); },
|
||||||
|
function() { return i18n.t('migration.migration-data-removed'); }
|
||||||
|
);
|
||||||
|
return response;
|
||||||
},
|
},
|
||||||
async import(folder, file) {
|
async import(folder, file) {
|
||||||
let response = await apiReq.post(migrationURLs.import(folder, file));
|
let response = await apiReq.post(migrationURLs.import(folder, file));
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { baseURL } from "./api-utils";
|
import { baseURL } from "./api-utils";
|
||||||
import { apiReq } from "./api-utils";
|
import { apiReq } from "./api-utils";
|
||||||
import { store } from "../store";
|
import { store } from "../store";
|
||||||
import { router } from "../main";
|
import i18n from '@/i18n.js';
|
||||||
|
|
||||||
const prefix = baseURL + "recipes/";
|
const prefix = baseURL + "recipes/";
|
||||||
|
|
||||||
@ -26,9 +26,12 @@ export const recipeAPI = {
|
|||||||
* @returns {string} Recipe Slug
|
* @returns {string} Recipe Slug
|
||||||
*/
|
*/
|
||||||
async createByURL(recipeURL) {
|
async createByURL(recipeURL) {
|
||||||
let response = await apiReq.post(recipeURLs.createByURL, {
|
const response = await apiReq.post(
|
||||||
url: recipeURL,
|
recipeURLs.createByURL,
|
||||||
});
|
{ url: recipeURL },
|
||||||
|
function() { return i18n.t('recipe.recipe-creation-failed'); },
|
||||||
|
function() { return i18n.t('recipe.recipe-created'); }
|
||||||
|
);
|
||||||
|
|
||||||
store.dispatch("requestRecentRecipes");
|
store.dispatch("requestRecentRecipes");
|
||||||
return response;
|
return response;
|
||||||
@ -43,7 +46,12 @@ export const recipeAPI = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async create(recipeData) {
|
async create(recipeData) {
|
||||||
let response = await apiReq.post(recipeURLs.create, recipeData);
|
const response = await apiReq.post(
|
||||||
|
recipeURLs.create,
|
||||||
|
recipeData,
|
||||||
|
function() { return i18n.t('recipe.recipe-creation-failed'); },
|
||||||
|
function() { return i18n.t('recipe.recipe-created'); }
|
||||||
|
);
|
||||||
store.dispatch("requestRecentRecipes");
|
store.dispatch("requestRecentRecipes");
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
@ -53,12 +61,22 @@ export const recipeAPI = {
|
|||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async updateImage(recipeSlug, fileObject) {
|
updateImage(recipeSlug, fileObject, overrideSuccessMsg = false) {
|
||||||
const fd = new FormData();
|
const formData = new FormData();
|
||||||
fd.append("image", fileObject);
|
formData.append("image", fileObject);
|
||||||
fd.append("extension", fileObject.name.split(".").pop());
|
formData.append("extension", fileObject.name.split(".").pop());
|
||||||
let response = apiReq.put(recipeURLs.updateImage(recipeSlug), fd);
|
|
||||||
return response;
|
let successMessage = null;
|
||||||
|
if(!overrideSuccessMsg) {
|
||||||
|
successMessage = function() { return overrideSuccessMsg ? null : i18n.t('recipe.recipe-image-updated'); };
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiReq.put(
|
||||||
|
recipeURLs.updateImage(recipeSlug),
|
||||||
|
formData,
|
||||||
|
function() { return i18n.t('general.image-upload-failed'); },
|
||||||
|
successMessage
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
async createAsset(recipeSlug, fileObject, name, icon) {
|
async createAsset(recipeSlug, fileObject, name, icon) {
|
||||||
@ -71,16 +89,26 @@ export const recipeAPI = {
|
|||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
|
|
||||||
async updateImagebyURL(slug, url) {
|
updateImagebyURL(slug, url) {
|
||||||
const response = apiReq.post(recipeURLs.updateImage(slug), { url: url });
|
return apiReq.post(
|
||||||
return response;
|
recipeURLs.updateImage(slug),
|
||||||
|
{ url: url },
|
||||||
|
function() { return i18n.t('general.image-upload-failed'); },
|
||||||
|
function() { return i18n.t('recipe.recipe-image-updated'); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
async update(data) {
|
async update(data) {
|
||||||
console.log(data)
|
let response = await apiReq.put(
|
||||||
let response = await apiReq.put(recipeURLs.update(data.slug), data);
|
recipeURLs.update(data.slug),
|
||||||
|
data,
|
||||||
|
function() { return i18n.t('recipe.recipe-update-failed'); },
|
||||||
|
function() { return i18n.t('recipe.recipe-updated'); }
|
||||||
|
);
|
||||||
|
if(response) {
|
||||||
store.dispatch("patchRecipe", response.data);
|
store.dispatch("patchRecipe", response.data);
|
||||||
return response.data.slug; // ! Temporary until I rewrite to refresh page without additional request
|
return response.data.slug; // ! Temporary until I rewrite to refresh page without additional request
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async patch(data) {
|
async patch(data) {
|
||||||
@ -89,10 +117,13 @@ export const recipeAPI = {
|
|||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async delete(recipeSlug) {
|
delete(recipeSlug) {
|
||||||
await apiReq.delete(recipeURLs.delete(recipeSlug));
|
return apiReq.delete(
|
||||||
store.dispatch("requestRecentRecipes");
|
recipeURLs.delete(recipeSlug),
|
||||||
router.push(`/`);
|
null,
|
||||||
|
function() { return i18n.t('recipe.unable-to-delete-recipe'); },
|
||||||
|
function() { return i18n.t('recipe.recipe-deleted'); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
async allSummary(start = 0, limit = 9999) {
|
async allSummary(start = 0, limit = 9999) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { baseURL } from "./api-utils";
|
import { baseURL } from "./api-utils";
|
||||||
import { apiReq } from "./api-utils";
|
import { apiReq } from "./api-utils";
|
||||||
|
import i18n from '@/i18n.js';
|
||||||
|
|
||||||
const signUpPrefix = baseURL + "users/sign-ups";
|
const signUpPrefix = baseURL + "users/sign-ups";
|
||||||
|
|
||||||
@ -16,15 +17,25 @@ export const signupAPI = {
|
|||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
async createToken(data) {
|
async createToken(data) {
|
||||||
let response = await apiReq.post(signUpURLs.createToken, data);
|
let response = await apiReq.post(
|
||||||
|
signUpURLs.createToken,
|
||||||
|
data,
|
||||||
|
function() { return i18n.t('signup.sign-up-link-creation-failed'); },
|
||||||
|
function() { return i18n.t('signup.sign-up-link-created'); }
|
||||||
|
);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
async deleteToken(token) {
|
async deleteToken(token) {
|
||||||
let response = await apiReq.delete(signUpURLs.deleteToken(token));
|
return await apiReq.delete(signUpURLs.deleteToken(token),
|
||||||
return response.data;
|
null,
|
||||||
|
function() { return i18n.t('signup.sign-up-token-deletion-failed'); },
|
||||||
|
function() { return i18n.t('signup.sign-up-token-deleted'); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
async createUser(token, data) {
|
async createUser(token, data) {
|
||||||
let response = await apiReq.post(signUpURLs.createUser(token), data);
|
return apiReq.post(signUpURLs.createUser(token), data,
|
||||||
return response.data;
|
function() { return i18n.t('user.you-are-not-allowed-to-create-a-user'); },
|
||||||
|
function() { return i18n.t('user.user-created'); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { baseURL } from "./api-utils";
|
import { baseURL } from "./api-utils";
|
||||||
import { apiReq } from "./api-utils";
|
import { apiReq } from "./api-utils";
|
||||||
import { store } from "@/store";
|
import { store } from "@/store";
|
||||||
|
import i18n from '@/i18n.js';
|
||||||
|
|
||||||
const settingsBase = baseURL + "site-settings";
|
const settingsBase = baseURL + "site-settings";
|
||||||
|
|
||||||
@ -19,9 +20,16 @@ export const siteSettingsAPI = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async update(body) {
|
async update(body) {
|
||||||
let response = await apiReq.put(settingsURLs.updateSiteSettings, body);
|
const response = await apiReq.put(
|
||||||
|
settingsURLs.updateSiteSettings,
|
||||||
|
body,
|
||||||
|
function() { return i18n.t('settings.settings-update-failed'); },
|
||||||
|
function() { return i18n.t('settings.settings-updated'); }
|
||||||
|
);
|
||||||
|
if(response) {
|
||||||
store.dispatch("requestSiteSettings");
|
store.dispatch("requestSiteSettings");
|
||||||
return response.data;
|
}
|
||||||
|
return response;
|
||||||
},
|
},
|
||||||
|
|
||||||
async getPages() {
|
async getPages() {
|
||||||
@ -34,23 +42,39 @@ export const siteSettingsAPI = {
|
|||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async createPage(body) {
|
createPage(body) {
|
||||||
let response = await apiReq.post(settingsURLs.customPages, body);
|
return apiReq.post(
|
||||||
return response.data;
|
settingsURLs.customPages,
|
||||||
|
body,
|
||||||
|
function() { return i18n.t('page.page-creation-failed'); },
|
||||||
|
function() { return i18n.t('page.new-page-created'); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
async deletePage(id) {
|
async deletePage(id) {
|
||||||
let response = await apiReq.delete(settingsURLs.customPage(id));
|
return await apiReq.delete(
|
||||||
return response.data;
|
settingsURLs.customPage(id),
|
||||||
|
null,
|
||||||
|
function() { return i18n.t('page.page-deletion-failed'); },
|
||||||
|
function() { return i18n.t('page.page-deleted'); });
|
||||||
},
|
},
|
||||||
|
|
||||||
async updatePage(body) {
|
updatePage(body) {
|
||||||
let response = await apiReq.put(settingsURLs.customPage(body.id), body);
|
return apiReq.put(
|
||||||
return response.data;
|
settingsURLs.customPage(body.id),
|
||||||
|
body,
|
||||||
|
function() { return i18n.t('page.page-update-failed'); },
|
||||||
|
function() { return i18n.t('page.page-updated'); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
async updateAllPages(allPages) {
|
async updateAllPages(allPages) {
|
||||||
let response = await apiReq.put(settingsURLs.customPages, allPages);
|
let response = await apiReq.put(
|
||||||
|
settingsURLs.customPages,
|
||||||
|
allPages,
|
||||||
|
function() { return i18n.t('page.pages-update-failed'); },
|
||||||
|
function() { return i18n.t('page.pages-updated'); }
|
||||||
|
);
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { baseURL } from "./api-utils";
|
import { baseURL } from "./api-utils";
|
||||||
import { apiReq } from "./api-utils";
|
import { apiReq } from "./api-utils";
|
||||||
|
import i18n from '@/i18n.js';
|
||||||
|
|
||||||
const prefix = baseURL + "themes";
|
const prefix = baseURL + "themes";
|
||||||
|
|
||||||
@ -23,21 +24,31 @@ export const themeAPI = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async create(postBody) {
|
async create(postBody) {
|
||||||
let response = await apiReq.post(settingsURLs.createTheme, postBody);
|
return await apiReq.post(
|
||||||
return response.data;
|
settingsURLs.createTheme,
|
||||||
|
postBody,
|
||||||
|
function() { return i18n.t('settings.theme.error-creating-theme-see-log-file'); },
|
||||||
|
function() { return i18n.t('settings.theme.theme-saved'); });
|
||||||
},
|
},
|
||||||
|
|
||||||
async update(themeName, colors) {
|
update(themeName, colors) {
|
||||||
const body = {
|
const body = {
|
||||||
name: themeName,
|
name: themeName,
|
||||||
colors: colors,
|
colors: colors,
|
||||||
};
|
};
|
||||||
let response = await apiReq.put(settingsURLs.updateTheme(themeName), body);
|
return apiReq.put(
|
||||||
return response.data;
|
settingsURLs.updateTheme(themeName),
|
||||||
|
body,
|
||||||
|
function() { return i18n.t('settings.theme.error-updating-theme'); },
|
||||||
|
function() { return i18n.t('settings.theme.theme-updated'); });
|
||||||
},
|
},
|
||||||
|
|
||||||
async delete(themeName) {
|
delete(themeName) {
|
||||||
let response = await apiReq.delete(settingsURLs.deleteTheme(themeName));
|
return apiReq.delete(
|
||||||
return response.data;
|
settingsURLs.deleteTheme(themeName),
|
||||||
|
null,
|
||||||
|
function() { return i18n.t('settings.theme.error-deleting-theme'); },
|
||||||
|
function() { return i18n.t('settings.theme.theme-deleted'); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import { apiReq } from "./api-utils";
|
import { apiReq } from "./api-utils";
|
||||||
|
import i18n from '@/i18n.js';
|
||||||
|
|
||||||
export const utilsAPI = {
|
export const utilsAPI = {
|
||||||
// import { api } from "@/api";
|
// import { api } from "@/api";
|
||||||
async uploadFile(url, fileObject) {
|
uploadFile(url, fileObject) {
|
||||||
console.log("API Called");
|
console.log("API Called");
|
||||||
|
|
||||||
let response = await apiReq.post(url, fileObject, {
|
return apiReq.post(
|
||||||
headers: {
|
url,
|
||||||
"Content-Type": "multipart/form-data",
|
fileObject,
|
||||||
},
|
function() { return i18n.t('general.failure-uploading-file'); },
|
||||||
});
|
function() { return i18n.t('general.file-uploaded'); }
|
||||||
return response.data;
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { baseURL } from "./api-utils";
|
import { baseURL } from "./api-utils";
|
||||||
import { apiReq } from "./api-utils";
|
import { apiReq } from "./api-utils";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import i18n from '@/i18n.js';
|
||||||
const authPrefix = baseURL + "auth";
|
const authPrefix = baseURL + "auth";
|
||||||
const userPrefix = baseURL + "users";
|
const userPrefix = baseURL + "users";
|
||||||
|
|
||||||
@ -17,13 +18,23 @@ const usersURLs = {
|
|||||||
resetPassword: id => `${userPrefix}/${id}/reset-password`,
|
resetPassword: id => `${userPrefix}/${id}/reset-password`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function deleteErrorText(response) {
|
||||||
|
switch(response.data.detail) {
|
||||||
|
case 'SUPER_USER':
|
||||||
|
return i18n.t('user.error-cannot-delete-super-user');
|
||||||
|
|
||||||
|
default:
|
||||||
|
return i18n.t('user.you-are-not-allowed-to-delete-this-user');
|
||||||
|
}
|
||||||
|
}
|
||||||
export const userAPI = {
|
export const userAPI = {
|
||||||
async login(formData) {
|
async login(formData) {
|
||||||
let response = await apiReq.post(authURLs.token, formData, {
|
let response = await apiReq.post(
|
||||||
headers: {
|
authURLs.token,
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
formData,
|
||||||
},
|
function() { return i18n.t('user.incorrect-username-or-password'); },
|
||||||
});
|
function() { return i18n.t('user.user-successfully-logged-in'); }
|
||||||
|
);
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
async refresh() {
|
async refresh() {
|
||||||
@ -36,9 +47,13 @@ export const userAPI = {
|
|||||||
let response = await apiReq.get(usersURLs.users);
|
let response = await apiReq.get(usersURLs.users);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
async create(user) {
|
create(user) {
|
||||||
let response = await apiReq.post(usersURLs.users, user);
|
return apiReq.post(
|
||||||
return response.data;
|
usersURLs.users,
|
||||||
|
user,
|
||||||
|
function() { return i18n.t('user.user-creation-failed'); },
|
||||||
|
function() { return i18n.t('user.user-created'); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
async self() {
|
async self() {
|
||||||
let response = await apiReq.get(usersURLs.self);
|
let response = await apiReq.get(usersURLs.self);
|
||||||
@ -48,20 +63,37 @@ export const userAPI = {
|
|||||||
let response = await apiReq.get(usersURLs.userID(id));
|
let response = await apiReq.get(usersURLs.userID(id));
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
async update(user) {
|
update(user) {
|
||||||
let response = await apiReq.put(usersURLs.userID(user.id), user);
|
return apiReq.put(
|
||||||
return response.data;
|
usersURLs.userID(user.id),
|
||||||
|
user,
|
||||||
|
function() { return i18n.t('user.user-update-failed'); },
|
||||||
|
function() { return i18n.t('user.user-updated'); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
async changePassword(id, password) {
|
changePassword(id, password) {
|
||||||
let response = await apiReq.put(usersURLs.password(id), password);
|
return apiReq.put(
|
||||||
return response.data;
|
usersURLs.password(id),
|
||||||
|
password,
|
||||||
|
function() { return i18n.t('user.existing-password-does-not-match'); },
|
||||||
|
function() { return i18n.t('user.password-updated'); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
async delete(id) {
|
|
||||||
let response = await apiReq.delete(usersURLs.userID(id));
|
delete(id) {
|
||||||
return response.data;
|
return apiReq.delete(
|
||||||
|
usersURLs.userID(id),
|
||||||
|
null,
|
||||||
|
deleteErrorText,
|
||||||
|
function() { return i18n.t('user.user-deleted'); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
async resetPassword(id) {
|
resetPassword(id) {
|
||||||
let response = await apiReq.put(usersURLs.resetPassword(id));
|
return apiReq.put(
|
||||||
return response.data;
|
usersURLs.resetPassword(id),
|
||||||
|
null,
|
||||||
|
function() { return i18n.t('user.password-reset-failed'); },
|
||||||
|
function() { return i18n.t('user.password-has-been-reset-to-the-default-password'); }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -90,7 +90,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
inputLabel() {
|
inputLabel() {
|
||||||
if (!this.showLabel) return null;
|
if (!this.showLabel) return null;
|
||||||
return this.tagSelector ? this.$t('recipe.tags') : this.$t('recipe.categories');
|
return this.tagSelector ? this.$t('tag.tags') : this.$t('recipe.categories');
|
||||||
},
|
},
|
||||||
activeItems() {
|
activeItems() {
|
||||||
let ItemObjects = [];
|
let ItemObjects = [];
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
mdi-import
|
mdi-import
|
||||||
</v-icon>
|
</v-icon>
|
||||||
<v-toolbar-title class="headline">
|
<v-toolbar-title class="headline">
|
||||||
Import Summary
|
{{ $t("settings.backup.import-summary") }}
|
||||||
</v-toolbar-title>
|
</v-toolbar-title>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
@ -18,8 +18,8 @@
|
|||||||
<div>
|
<div>
|
||||||
<h3>{{ values.title }}</h3>
|
<h3>{{ values.title }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="success--text">Success: {{ values.success }}</div>
|
<div class="success--text">{{ $t("general.success-count", { count: values.success }) }}</div>
|
||||||
<div class="error--text">Failed: {{ values.failure }}</div>
|
<div class="error--text">{{ $t("general.failed-count", { count: values.failure }) }}</div>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</div>
|
</div>
|
||||||
</v-row>
|
</v-row>
|
||||||
@ -28,7 +28,7 @@
|
|||||||
<v-tab>{{ $t("general.recipes") }}</v-tab>
|
<v-tab>{{ $t("general.recipes") }}</v-tab>
|
||||||
<v-tab>{{ $t("general.themes") }}</v-tab>
|
<v-tab>{{ $t("general.themes") }}</v-tab>
|
||||||
<v-tab>{{ $t("general.settings") }}</v-tab>
|
<v-tab>{{ $t("general.settings") }}</v-tab>
|
||||||
<v-tab> Pages </v-tab>
|
<v-tab> {{ $t("settings.pages") }} </v-tab>
|
||||||
<v-tab>{{ $t("general.users") }}</v-tab>
|
<v-tab>{{ $t("general.users") }}</v-tab>
|
||||||
<v-tab>{{ $t("general.groups") }}</v-tab>
|
<v-tab>{{ $t("general.groups") }}</v-tab>
|
||||||
</v-tabs>
|
</v-tabs>
|
||||||
@ -59,24 +59,30 @@ export default {
|
|||||||
userData: [],
|
userData: [],
|
||||||
groupData: [],
|
groupData: [],
|
||||||
pageData: [],
|
pageData: [],
|
||||||
importHeaders: [
|
|
||||||
{
|
|
||||||
text: "Status",
|
|
||||||
value: "status",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "Name",
|
|
||||||
align: "start",
|
|
||||||
sortable: true,
|
|
||||||
value: "name",
|
|
||||||
},
|
|
||||||
|
|
||||||
{ text: "Exception", value: "data-table-expand", align: "center" },
|
|
||||||
],
|
|
||||||
allDataTables: [],
|
allDataTables: [],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
|
||||||
|
importHeaders() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
text: this.$t('general.status'),
|
||||||
|
value: "status",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: this.$t('general.name'),
|
||||||
|
align: "start",
|
||||||
|
sortable: true,
|
||||||
|
value: "name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: this.$t('general.exception'),
|
||||||
|
value: "data-table-expand",
|
||||||
|
align: "center"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
recipeNumbers() {
|
recipeNumbers() {
|
||||||
return this.calculateNumbers(this.$t("general.recipes"), this.recipeData);
|
return this.calculateNumbers(this.$t("general.recipes"), this.recipeData);
|
||||||
},
|
},
|
||||||
@ -96,7 +102,7 @@ export default {
|
|||||||
return this.calculateNumbers(this.$t("general.groups"), this.groupData);
|
return this.calculateNumbers(this.$t("general.groups"), this.groupData);
|
||||||
},
|
},
|
||||||
pageNumbers() {
|
pageNumbers() {
|
||||||
return this.calculateNumbers("Pages", this.pageData);
|
return this.calculateNumbers(this.$t("settings.pages"), this.pageData);
|
||||||
},
|
},
|
||||||
allNumbers() {
|
allNumbers() {
|
||||||
return [
|
return [
|
||||||
|
@ -91,18 +91,12 @@ export default {
|
|||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
formData.append("username", this.user.email);
|
formData.append("username", this.user.email);
|
||||||
formData.append("password", this.user.password);
|
formData.append("password", this.user.password);
|
||||||
let key;
|
const response = await api.users.login(formData);
|
||||||
try {
|
if (!response) {
|
||||||
key = await api.users.login(formData);
|
|
||||||
} catch {
|
|
||||||
this.error = true;
|
this.error = true;
|
||||||
}
|
|
||||||
if (key.status != 200) {
|
|
||||||
this.error = true;
|
|
||||||
this.loading = false;
|
|
||||||
} else {
|
} else {
|
||||||
this.clear();
|
this.clear();
|
||||||
this.$store.commit("setToken", key.data.access_token);
|
this.$store.commit("setToken", response.data.access_token);
|
||||||
this.$emit("logged-in");
|
this.$emit("logged-in");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,13 +13,13 @@
|
|||||||
class="mr-2"
|
class="mr-2"
|
||||||
>
|
>
|
||||||
</v-progress-circular>
|
</v-progress-circular>
|
||||||
<v-toolbar-title class="headline"> Sign Up </v-toolbar-title>
|
<v-toolbar-title class="headline">
|
||||||
|
{{$t('signup.sign-up')}}
|
||||||
|
</v-toolbar-title>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
Welcome to Mealie! To become a user of this instance you are required to
|
{{$t('signup.welcome-to-mealie')}}
|
||||||
have a valid invitation link. If you haven't recieved an invitation you
|
|
||||||
are unable to sign-up. To recieve a link, contact the sites administrator.
|
|
||||||
<v-divider class="mt-3"></v-divider>
|
<v-divider class="mt-3"></v-divider>
|
||||||
<v-form ref="signUpForm" @submit.prevent="signUp">
|
<v-form ref="signUpForm" @submit.prevent="signUp">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@ -28,7 +28,7 @@
|
|||||||
prepend-icon="mdi-account"
|
prepend-icon="mdi-account"
|
||||||
validate-on-blur
|
validate-on-blur
|
||||||
:rules="[existsRule]"
|
:rules="[existsRule]"
|
||||||
label="Display Name"
|
:label="$t('signup.display-name')"
|
||||||
type="email"
|
type="email"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@ -59,7 +59,7 @@
|
|||||||
:type="showPassword ? 'text' : 'password'"
|
:type="showPassword ? 'text' : 'password'"
|
||||||
:append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'"
|
:append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'"
|
||||||
:rules="[
|
:rules="[
|
||||||
user.password === user.passwordConfirm || 'Password must match',
|
user.password === user.passwordConfirm || $t('user.password-must-match'),
|
||||||
]"
|
]"
|
||||||
@click:append="showPassword = !showPassword"
|
@click:append="showPassword = !showPassword"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
@ -71,11 +71,11 @@
|
|||||||
block="block"
|
block="block"
|
||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
Sign Up
|
{{$t('signup.sign-up')}}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
<v-alert dense v-if="error" outlined class="mt-3 mb-0" type="error">
|
<v-alert dense v-if="error" outlined class="mt-3 mb-0" type="error">
|
||||||
Error Signing Up
|
{{$t('signup.error-signing-up')}}
|
||||||
</v-alert>
|
</v-alert>
|
||||||
</v-form>
|
</v-form>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
@ -132,18 +132,16 @@ export default {
|
|||||||
admin: false,
|
admin: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let successUser = false;
|
|
||||||
if (this.$refs.signUpForm.validate()) {
|
if (this.$refs.signUpForm.validate()) {
|
||||||
let response = await api.signUps.createUser(this.token, userData);
|
if (await api.signUps.createUser(this.token, userData)) {
|
||||||
successUser = response.snackbar.text.includes("Created");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$emit("user-created");
|
this.$emit("user-created");
|
||||||
|
|
||||||
this.loading = false;
|
|
||||||
if (successUser) {
|
|
||||||
this.$router.push("/");
|
this.$router.push("/");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -36,8 +36,9 @@ export default {
|
|||||||
return utils.getDateAsPythonDate(dateObject);
|
return utils.getDateAsPythonDate(dateObject);
|
||||||
},
|
},
|
||||||
async update() {
|
async update() {
|
||||||
await api.mealPlans.update(this.mealPlan.uid, this.mealPlan);
|
if (await api.mealPlans.update(this.mealPlan.uid, this.mealPlan)) {
|
||||||
this.$emit("updated");
|
this.$emit("updated");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -197,11 +197,12 @@ export default {
|
|||||||
endDate: this.endDate,
|
endDate: this.endDate,
|
||||||
meals: this.meals,
|
meals: this.meals,
|
||||||
};
|
};
|
||||||
await api.mealPlans.create(mealBody);
|
if (await api.mealPlans.create(mealBody)) {
|
||||||
this.$emit(CREATE_EVENT);
|
this.$emit(CREATE_EVENT);
|
||||||
this.meals = [];
|
this.meals = [];
|
||||||
this.startDate = null;
|
this.startDate = null;
|
||||||
this.endDate = null;
|
this.endDate = null;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getImage(image) {
|
getImage(image) {
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<v-icon left>
|
<v-icon left>
|
||||||
mdi-image
|
mdi-image
|
||||||
</v-icon>
|
</v-icon>
|
||||||
{{ $t("recipe.image") }}
|
{{ $t("general.image") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<v-card width="400">
|
<v-card width="400">
|
||||||
@ -71,8 +71,9 @@ export default {
|
|||||||
},
|
},
|
||||||
async getImageFromURL() {
|
async getImageFromURL() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
const response = await api.recipes.updateImagebyURL(this.slug, this.url);
|
if (await api.recipes.updateImagebyURL(this.slug, this.url)) {
|
||||||
if (response) this.$emit(REFRESH_EVENT);
|
this.$emit(REFRESH_EVENT);
|
||||||
|
}
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
:show-label="false"
|
:show-label="false"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<h2 class="mt-4">{{ $t("recipe.tags") }}</h2>
|
<h2 class="mt-4">{{ $t("tag.tags") }}</h2>
|
||||||
<CategoryTagSelector
|
<CategoryTagSelector
|
||||||
:return-object="false"
|
:return-object="false"
|
||||||
v-model="value.tags"
|
v-model="value.tags"
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
</v-card>
|
</v-card>
|
||||||
<v-card class="mt-2" v-if="tags.length > 0">
|
<v-card class="mt-2" v-if="tags.length > 0">
|
||||||
<v-card-title class="py-2">
|
<v-card-title class="py-2">
|
||||||
{{ $t("recipe.tags") }}
|
{{ $t("tag.tags") }}
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-divider class="mx-2"></v-divider>
|
<v-divider class="mx-2"></v-divider>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
@ -69,7 +69,7 @@
|
|||||||
</v-row>
|
</v-row>
|
||||||
<div v-if="!medium">
|
<div v-if="!medium">
|
||||||
<RecipeChips :title="$t('recipe.categories')" :items="categories" />
|
<RecipeChips :title="$t('recipe.categories')" :items="categories" />
|
||||||
<RecipeChips :title="$t('recipe.tags')" :items="tags" />
|
<RecipeChips :title="$t('tag.tags')" :items="tags" />
|
||||||
<Nutrition :value="nutrition" :edit="false" />
|
<Nutrition :value="nutrition" :edit="false" />
|
||||||
<Assets :value="assets" :edit="false" :slug="slug" />
|
<Assets :value="assets" :edit="false" :slug="slug" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -55,11 +55,11 @@ export default {
|
|||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
formData.append(this.fileName, this.file);
|
formData.append(this.fileName, this.file);
|
||||||
|
|
||||||
await api.utils.uploadFile(this.url, formData);
|
if(await api.utils.uploadFile(this.url, formData)) {
|
||||||
|
|
||||||
this.isSelecting = false;
|
|
||||||
this.$emit(UPLOAD_EVENT);
|
this.$emit(UPLOAD_EVENT);
|
||||||
}
|
}
|
||||||
|
this.isSelecting = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onButtonClick() {
|
onButtonClick() {
|
||||||
this.isSelecting = true;
|
this.isSelecting = true;
|
||||||
|
@ -105,17 +105,15 @@ export default {
|
|||||||
async createRecipe() {
|
async createRecipe() {
|
||||||
if (this.$refs.urlForm.validate()) {
|
if (this.$refs.urlForm.validate()) {
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
let response = await api.recipes.createByURL(this.recipeURL);
|
const response = await api.recipes.createByURL(this.recipeURL);
|
||||||
if (response.status !== 201) {
|
|
||||||
this.error = true;
|
|
||||||
this.processing = false;
|
this.processing = false;
|
||||||
return;
|
if (response) {
|
||||||
}
|
|
||||||
|
|
||||||
this.addRecipe = false;
|
this.addRecipe = false;
|
||||||
this.processing = false;
|
|
||||||
this.recipeURL = "";
|
this.recipeURL = "";
|
||||||
this.$router.push(`/recipe/${response.data}`);
|
this.$router.push(`/recipe/${response.data}`);
|
||||||
|
} else {
|
||||||
|
this.error = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -44,9 +44,9 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
LoginDialog,
|
LoginDialog,
|
||||||
},
|
},
|
||||||
data: function() {
|
computed: {
|
||||||
return {
|
items() {
|
||||||
items: [
|
return [
|
||||||
{
|
{
|
||||||
icon: "mdi-account",
|
icon: "mdi-account",
|
||||||
title: "Login",
|
title: "Login",
|
||||||
@ -83,10 +83,8 @@ export default {
|
|||||||
nav: "/admin",
|
nav: "/admin",
|
||||||
restricted: true,
|
restricted: true,
|
||||||
},
|
},
|
||||||
],
|
]
|
||||||
};
|
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
filteredItems() {
|
filteredItems() {
|
||||||
if (this.loggedIn) {
|
if (this.loggedIn) {
|
||||||
return this.items.filter(x => x.restricted == true);
|
return this.items.filter(x => x.restricted == true);
|
||||||
|
@ -13,11 +13,22 @@
|
|||||||
"demo": "Demo",
|
"demo": "Demo",
|
||||||
"demo-status": "Demo Status",
|
"demo-status": "Demo Status",
|
||||||
"development": "Development",
|
"development": "Development",
|
||||||
|
"download-log": "Download Log",
|
||||||
|
"download-recipe-json": "Download Recipe JSON",
|
||||||
"not-demo": "Not Demo",
|
"not-demo": "Not Demo",
|
||||||
"production": "Production",
|
"production": "Production",
|
||||||
"sqlite-file": "SQLite File",
|
"sqlite-file": "SQLite File",
|
||||||
"version": "Version"
|
"version": "Version"
|
||||||
},
|
},
|
||||||
|
"category": {
|
||||||
|
"category-created": "Category created",
|
||||||
|
"category-creation-failed": "Category creation failed",
|
||||||
|
"category-deleted": "Category Deleted",
|
||||||
|
"category-deletion-failed": "Category deletion failed",
|
||||||
|
"category-filter": "Category Filter",
|
||||||
|
"category-update-failed": "Category update failed",
|
||||||
|
"category-updated": "Category updated"
|
||||||
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"apply": "Apply",
|
"apply": "Apply",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
@ -30,29 +41,40 @@
|
|||||||
"download": "Download",
|
"download": "Download",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"enabled": "Enabled",
|
"enabled": "Enabled",
|
||||||
|
"exception": "Exception",
|
||||||
|
"failed-count": "Failed: {count}",
|
||||||
|
"failure-uploading-file": "Failure uploading file",
|
||||||
"field-required": "Field Required",
|
"field-required": "Field Required",
|
||||||
|
"file-folder-not-found": "File/folder not found",
|
||||||
|
"file-uploaded": "File uploaded",
|
||||||
"filter": "Filter",
|
"filter": "Filter",
|
||||||
"friday": "Friday",
|
"friday": "Friday",
|
||||||
"get": "Get",
|
"get": "Get",
|
||||||
"groups": "Groups",
|
"groups": "Groups",
|
||||||
|
"image": "Image",
|
||||||
|
"image-upload-failed": "Image upload failed",
|
||||||
"import": "Import",
|
"import": "Import",
|
||||||
|
"keyword": "Keyword",
|
||||||
"monday": "Monday",
|
"monday": "Monday",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
"ok": "OK",
|
"ok": "OK",
|
||||||
"options": "Options",
|
"options": "Options:",
|
||||||
"random": "Random",
|
"random": "Random",
|
||||||
"recent": "Recent",
|
"recent": "Recent",
|
||||||
"recipes": "Recipes",
|
"recipes": "Recipes",
|
||||||
|
"rename-object": "Rename {0}",
|
||||||
"reset": "Reset",
|
"reset": "Reset",
|
||||||
"saturday": "Saturday",
|
"saturday": "Saturday",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"sort": "Sort",
|
"sort": "Sort",
|
||||||
"sort-alphabetically": "A-Z",
|
"sort-alphabetically": "A-Z",
|
||||||
|
"status": "Status",
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
|
"success-count": "Success: {count}",
|
||||||
"sunday": "Sunday",
|
"sunday": "Sunday",
|
||||||
"templates": "Templates",
|
"templates": "Templates:",
|
||||||
"themes": "Themes",
|
"themes": "Themes",
|
||||||
"thursday": "Thursday",
|
"thursday": "Thursday",
|
||||||
"token": "Token",
|
"token": "Token",
|
||||||
@ -64,6 +86,25 @@
|
|||||||
"wednesday": "Wednesday",
|
"wednesday": "Wednesday",
|
||||||
"yes": "Yes"
|
"yes": "Yes"
|
||||||
},
|
},
|
||||||
|
"group": {
|
||||||
|
"are-you-sure-you-want-to-delete-the-group": "Are you sure you want to delete <b>{groupName}<b/>?",
|
||||||
|
"cannot-delete-default-group": "Cannot delete default group",
|
||||||
|
"cannot-delete-group-with-users": "Cannot delete group with users",
|
||||||
|
"confirm-group-deletion": "Confirm Group Deletion",
|
||||||
|
"create-group": "Create Group",
|
||||||
|
"error-updating-group": "Error updating group",
|
||||||
|
"group": "Group",
|
||||||
|
"group-deleted": "Group deleted",
|
||||||
|
"group-deletion-failed": "Group deletion failed",
|
||||||
|
"group-id-with-value": "Group ID: {groupID}",
|
||||||
|
"group-name": "Group Name",
|
||||||
|
"group-not-found": "Group not found",
|
||||||
|
"groups": "Groups",
|
||||||
|
"groups-can-only-be-set-by-administrators": "Groups can only be set by administrators",
|
||||||
|
"user-group": "User Group",
|
||||||
|
"user-group-created": "User Group Created",
|
||||||
|
"user-group-creation-failed": "User Group Creation Failed"
|
||||||
|
},
|
||||||
"meal-plan": {
|
"meal-plan": {
|
||||||
"create-a-new-meal-plan": "Create a New Meal Plan",
|
"create-a-new-meal-plan": "Create a New Meal Plan",
|
||||||
"dinner-this-week": "Dinner This Week",
|
"dinner-this-week": "Dinner This Week",
|
||||||
@ -73,6 +114,14 @@
|
|||||||
"group": "Group (Beta)",
|
"group": "Group (Beta)",
|
||||||
"meal-planner": "Meal Planner",
|
"meal-planner": "Meal Planner",
|
||||||
"meal-plans": "Meal Plans",
|
"meal-plans": "Meal Plans",
|
||||||
|
"mealplan-created": "Mealplan created",
|
||||||
|
"mealplan-creation-failed": "Mealplan creation failed",
|
||||||
|
"mealplan-deleted": "Mealplan Deleted",
|
||||||
|
"mealplan-deletion-failed": "Mealplan deletion failed",
|
||||||
|
"mealplan-update-failed": "Mealplan update failed",
|
||||||
|
"mealplan-updated": "Mealplan Updated",
|
||||||
|
"no-meal-plan-defined-yet": "No meal plan defined yet",
|
||||||
|
"no-meal-planned-for-today": "No meal planned for today",
|
||||||
"only-recipes-with-these-categories-will-be-used-in-meal-plans": "Only recipes with these categories will be used in Meal Plans",
|
"only-recipes-with-these-categories-will-be-used-in-meal-plans": "Only recipes with these categories will be used in Meal Plans",
|
||||||
"planner": "Planner",
|
"planner": "Planner",
|
||||||
"quick-week": "Quick Week",
|
"quick-week": "Quick Week",
|
||||||
@ -84,6 +133,7 @@
|
|||||||
"description": "Migrate data from Chowdown",
|
"description": "Migrate data from Chowdown",
|
||||||
"title": "Chowdown"
|
"title": "Chowdown"
|
||||||
},
|
},
|
||||||
|
"migration-data-removed": "Migration data removed",
|
||||||
"nextcloud": {
|
"nextcloud": {
|
||||||
"description": "Migrate data from a Nextcloud Cookbook intance",
|
"description": "Migrate data from a Nextcloud Cookbook intance",
|
||||||
"title": "Nextcloud Cookbook"
|
"title": "Nextcloud Cookbook"
|
||||||
@ -102,23 +152,30 @@
|
|||||||
"page": {
|
"page": {
|
||||||
"all-recipes": "All Recipes",
|
"all-recipes": "All Recipes",
|
||||||
"home-page": "Home Page",
|
"home-page": "Home Page",
|
||||||
|
"new-page-created": "New page created",
|
||||||
|
"page-creation-failed": "Page creation failed",
|
||||||
|
"page-deleted": "Page deleted",
|
||||||
|
"page-deletion-failed": "Page deletion failed",
|
||||||
|
"page-update-failed": "Page update failed",
|
||||||
|
"page-updated": "Page updated",
|
||||||
|
"pages-update-failed": "Pages update failed",
|
||||||
|
"pages-updated": "Pages updated",
|
||||||
"recent": "Recent"
|
"recent": "Recent"
|
||||||
},
|
},
|
||||||
"recipe": {
|
"recipe": {
|
||||||
"assets": "Assets",
|
|
||||||
"add-key": "Add Key",
|
"add-key": "Add Key",
|
||||||
"api-extras": "API Extras",
|
"api-extras": "API Extras",
|
||||||
|
"assets": "Assets",
|
||||||
"calories": "Calories",
|
"calories": "Calories",
|
||||||
"calories-suffix": "calories",
|
"calories-suffix": "calories",
|
||||||
|
"carbohydrate-content": "Carbohydrate",
|
||||||
"categories": "Categories",
|
"categories": "Categories",
|
||||||
"delete-confirmation": "Are you sure you want to delete this recipe?",
|
"delete-confirmation": "Are you sure you want to delete this recipe?",
|
||||||
"delete-recipe": "Delete Recipe",
|
"delete-recipe": "Delete Recipe",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
"fat-content": "Fat",
|
"fat-content": "Fat",
|
||||||
"fiber-content": "Fiber",
|
"fiber-content": "Fiber",
|
||||||
"carbohydrate-content": "Carbohydrate",
|
|
||||||
"grams": "grams",
|
"grams": "grams",
|
||||||
"image": "Image",
|
|
||||||
"ingredient": "Ingredient",
|
"ingredient": "Ingredient",
|
||||||
"ingredients": "Ingredients",
|
"ingredients": "Ingredients",
|
||||||
"instructions": "Instructions",
|
"instructions": "Instructions",
|
||||||
@ -135,27 +192,32 @@
|
|||||||
"perform-time": "Cook Time",
|
"perform-time": "Cook Time",
|
||||||
"prep-time": "Prep Time",
|
"prep-time": "Prep Time",
|
||||||
"protein-content": "Protein",
|
"protein-content": "Protein",
|
||||||
|
"recipe-created": "Recipe created",
|
||||||
|
"recipe-creation-failed": "Recipe creation failed",
|
||||||
|
"recipe-deleted": "Recipe deleted",
|
||||||
"recipe-image": "Recipe Image",
|
"recipe-image": "Recipe Image",
|
||||||
|
"recipe-image-updated": "Recipe image updated",
|
||||||
"recipe-name": "Recipe Name",
|
"recipe-name": "Recipe Name",
|
||||||
|
"recipe-update-failed": "Recipe update failed",
|
||||||
|
"recipe-updated": "Recipe updated",
|
||||||
"servings": "Servings",
|
"servings": "Servings",
|
||||||
"sodium-content": "Sodium",
|
"sodium-content": "Sodium",
|
||||||
"step-index": "Step: {step}",
|
"step-index": "Step: {step}",
|
||||||
"sugar-content": "Sugar",
|
"sugar-content": "Sugar",
|
||||||
"tags": "Tags",
|
|
||||||
"title": "Title",
|
"title": "Title",
|
||||||
"total-time": "Total Time",
|
"total-time": "Total Time",
|
||||||
|
"unable-to-delete-recipe": "Unable to Delete Recipe",
|
||||||
"view-recipe": "View Recipe"
|
"view-recipe": "View Recipe"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"search-mealie": "Search Mealie (press /)",
|
"and": "and",
|
||||||
"search-placeholder": "Search...",
|
|
||||||
"max-results": "Max Results",
|
|
||||||
"category-filter": "Category Filter",
|
|
||||||
"exclude": "Exclude",
|
"exclude": "Exclude",
|
||||||
"include": "Include",
|
"include": "Include",
|
||||||
|
"max-results": "Max Results",
|
||||||
"or": "Or",
|
"or": "Or",
|
||||||
"and": "and",
|
|
||||||
"search": "Search",
|
"search": "Search",
|
||||||
|
"search-mealie": "Search Mealie (press /)",
|
||||||
|
"search-placeholder": "Search...",
|
||||||
"tag-filter": "Tag Filter"
|
"tag-filter": "Tag Filter"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
@ -163,10 +225,15 @@
|
|||||||
"admin-settings": "Admin Settings",
|
"admin-settings": "Admin Settings",
|
||||||
"available-backups": "Available Backups",
|
"available-backups": "Available Backups",
|
||||||
"backup": {
|
"backup": {
|
||||||
|
"backup-created-at-response-export_path": "Backup Created at {path}",
|
||||||
|
"backup-deleted": "Backup deleted",
|
||||||
"backup-tag": "Backup Tag",
|
"backup-tag": "Backup Tag",
|
||||||
"create-heading": "Create a Backup",
|
"create-heading": "Create a Backup",
|
||||||
|
"error-creating-backup-see-log-file": "Error Creating Backup. See Log File",
|
||||||
"full-backup": "Full Backup",
|
"full-backup": "Full Backup",
|
||||||
"partial-backup": "Partial Backup"
|
"import-summary": "Import Summary",
|
||||||
|
"partial-backup": "Partial Backup",
|
||||||
|
"unable-to-delete-backup": "Unable to Delete Backup."
|
||||||
},
|
},
|
||||||
"backup-and-exports": "Backups",
|
"backup-and-exports": "Backups",
|
||||||
"backup-info": "Backups are exported in standard JSON format along with all the images stored on the file system. In your backup folder you'll find a .zip file that contains all of the recipe JSON and images from the database. Additionally, if you selected a markdown file, those will also be stored in the .zip file. To import a backup, it must be located in your backups folder. Automated backups are done each day at 3:00 AM.",
|
"backup-info": "Backups are exported in standard JSON format along with all the images stored on the file system. In your backup folder you'll find a .zip file that contains all of the recipe JSON and images from the database. Additionally, if you selected a markdown file, those will also be stored in the .zip file. To import a backup, it must be located in your backups folder. Automated backups are done each day at 3:00 AM.",
|
||||||
@ -175,6 +242,7 @@
|
|||||||
"custom-pages": "Custom Pages",
|
"custom-pages": "Custom Pages",
|
||||||
"edit-page": "Edit Page",
|
"edit-page": "Edit Page",
|
||||||
"first-day-of-week": "First day of the week",
|
"first-day-of-week": "First day of the week",
|
||||||
|
"group-settings-updated": "Group Settings Updated",
|
||||||
"homepage": {
|
"homepage": {
|
||||||
"all-categories": "All Categories",
|
"all-categories": "All Categories",
|
||||||
"card-per-section": "Card Per Section",
|
"card-per-section": "Card Per Section",
|
||||||
@ -190,9 +258,12 @@
|
|||||||
"migrations": "Migrations",
|
"migrations": "Migrations",
|
||||||
"new-page": "New Page",
|
"new-page": "New Page",
|
||||||
"page-name": "Page Name",
|
"page-name": "Page Name",
|
||||||
|
"pages": "Pages",
|
||||||
"profile": "Profile",
|
"profile": "Profile",
|
||||||
"remove-existing-entries-matching-imported-entries": "Remove existing entries matching imported entries",
|
"remove-existing-entries-matching-imported-entries": "Remove existing entries matching imported entries",
|
||||||
"set-new-time": "Set New Time",
|
"set-new-time": "Set New Time",
|
||||||
|
"settings-update-failed": "Settings update failed",
|
||||||
|
"settings-updated": "Settings updated",
|
||||||
"site-settings": "Site Settings",
|
"site-settings": "Site Settings",
|
||||||
"theme": {
|
"theme": {
|
||||||
"accent": "Accent",
|
"accent": "Accent",
|
||||||
@ -203,6 +274,9 @@
|
|||||||
"default-to-system": "Default to system",
|
"default-to-system": "Default to system",
|
||||||
"delete-theme": "Delete Theme",
|
"delete-theme": "Delete Theme",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
|
"error-creating-theme-see-log-file": "Error creating theme. See log file.",
|
||||||
|
"error-deleting-theme": "Error deleting theme",
|
||||||
|
"error-updating-theme": "Error updating theme",
|
||||||
"info": "Info",
|
"info": "Info",
|
||||||
"light": "Light",
|
"light": "Light",
|
||||||
"primary": "Primary",
|
"primary": "Primary",
|
||||||
@ -210,51 +284,69 @@
|
|||||||
"select-a-theme-from-the-dropdown-or-create-a-new-theme-note-that-the-default-theme-will-be-served-to-all-users-who-have-not-set-a-theme-preference": "Select a theme from the dropdown or create a new theme. Note that the default theme will be served to all users who have not set a theme preference.",
|
"select-a-theme-from-the-dropdown-or-create-a-new-theme-note-that-the-default-theme-will-be-served-to-all-users-who-have-not-set-a-theme-preference": "Select a theme from the dropdown or create a new theme. Note that the default theme will be served to all users who have not set a theme preference.",
|
||||||
"success": "Success",
|
"success": "Success",
|
||||||
"theme": "Theme",
|
"theme": "Theme",
|
||||||
|
"theme-deleted": "Theme deleted",
|
||||||
"theme-name": "Theme Name",
|
"theme-name": "Theme Name",
|
||||||
"theme-name-is-required": "Theme Name is required.",
|
"theme-name-is-required": "Theme Name is required.",
|
||||||
|
"theme-saved": "Theme Saved",
|
||||||
"theme-settings": "Theme Settings",
|
"theme-settings": "Theme Settings",
|
||||||
|
"theme-updated": "Theme updated",
|
||||||
"warning": "Warning"
|
"warning": "Warning"
|
||||||
},
|
},
|
||||||
|
"toolbox": {
|
||||||
|
"assign-all": "Assign All",
|
||||||
|
"bulk-assign": "Bulk Assign",
|
||||||
|
"new-name": "New Name",
|
||||||
|
"no-unused-items": "No Unused Items",
|
||||||
|
"recipes-affected": "No Recipes Affected|One Recipe Affected|{count} Recipes Affected",
|
||||||
|
"remove-unused": "Remove Unused",
|
||||||
|
"title-case-all": "Title Case All",
|
||||||
|
"toolbox": "Toolbox"
|
||||||
|
},
|
||||||
"webhooks": {
|
"webhooks": {
|
||||||
"meal-planner-webhooks": "Meal Planner Webhooks",
|
"meal-planner-webhooks": "Meal Planner Webhooks",
|
||||||
"test-webhooks": "Test Webhooks",
|
"test-webhooks": "Test Webhooks",
|
||||||
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
|
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
|
||||||
"webhook-url": "Webhook URL"
|
"webhook-url": "Webhook URL"
|
||||||
},
|
|
||||||
"toolbox": {
|
|
||||||
"toolbox": "Toolbox",
|
|
||||||
"new-name": "New Name",
|
|
||||||
"recipes-effected": "Recipes Effected",
|
|
||||||
"title-case-all": "Title Case All",
|
|
||||||
"no-unused-items": "No Unused Items",
|
|
||||||
"remove-unused": "Remove Unused",
|
|
||||||
"assign-all": "Assign All",
|
|
||||||
"bulk-assign": "Bulk Assign"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"signup": {
|
||||||
|
"display-name": "Display Name",
|
||||||
|
"error-signing-up": "Error Signing Up",
|
||||||
|
"sign-up": "Sign Up",
|
||||||
|
"sign-up-link-created": "Sign up link created",
|
||||||
|
"sign-up-link-creation-failed": "Sign up link creation failed",
|
||||||
|
"sign-up-links": "Sign Up Links",
|
||||||
|
"sign-up-token-deleted": "Sign Up Token Deleted",
|
||||||
|
"sign-up-token-deletion-failed": "Sign up token deletion failed",
|
||||||
|
"welcome-to-mealie": "Welcome to Mealie! To become a user of this instance you are required to have a valid invitation link. If you haven't recieved an invitation you are unable to sign-up. To recieve a link, contact the sites administrator."
|
||||||
|
},
|
||||||
|
"tag": {
|
||||||
|
"tag-created": "Tag created",
|
||||||
|
"tag-creation-failed": "Tag creation failed",
|
||||||
|
"tag-deleted": "Tag deleted",
|
||||||
|
"tag-deletion-failed": "Tag deletion failed",
|
||||||
|
"tag-update-failed": "Tag update failed",
|
||||||
|
"tag-updated": "Tag updated",
|
||||||
|
"tags": "Tags"
|
||||||
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"admin": "Admin",
|
"admin": "Admin",
|
||||||
"are-you-sure-you-want-to-delete-the-group": "Are you sure you want to delete <b>{groupName}<b/>?",
|
|
||||||
"are-you-sure-you-want-to-delete-the-link": "Are you sure you want to delete the link <b>{link}<b/>?",
|
"are-you-sure-you-want-to-delete-the-link": "Are you sure you want to delete the link <b>{link}<b/>?",
|
||||||
"are-you-sure-you-want-to-delete-the-user": "Are you sure you want to delete the user <b>{activeName} ID: {activeId}<b/>?",
|
"are-you-sure-you-want-to-delete-the-user": "Are you sure you want to delete the user <b>{activeName} ID: {activeId}<b/>?",
|
||||||
"confirm-group-deletion": "Confirm Group Deletion",
|
|
||||||
"confirm-link-deletion": "Confirm Link Deletion",
|
"confirm-link-deletion": "Confirm Link Deletion",
|
||||||
"confirm-password": "Confirm Password",
|
"confirm-password": "Confirm Password",
|
||||||
"confirm-user-deletion": "Confirm User Deletion",
|
"confirm-user-deletion": "Confirm User Deletion",
|
||||||
"could-not-validate-credentials": "Could Not Validate Credentials",
|
"could-not-validate-credentials": "Could Not Validate Credentials",
|
||||||
"create-group": "Create Group",
|
|
||||||
"create-link": "Create Link",
|
"create-link": "Create Link",
|
||||||
"create-user": "Create User",
|
"create-user": "Create User",
|
||||||
"current-password": "Current Password",
|
"current-password": "Current Password",
|
||||||
"e-mail-must-be-valid": "E-mail must be valid",
|
"e-mail-must-be-valid": "E-mail must be valid",
|
||||||
"edit-user": "Edit User",
|
"edit-user": "Edit User",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
|
"error-cannot-delete-super-user": "Error! Cannot Delete Super User",
|
||||||
|
"existing-password-does-not-match": "Existing password does not match",
|
||||||
"full-name": "Full Name",
|
"full-name": "Full Name",
|
||||||
"group": "Group",
|
"incorrect-username-or-password": "Incorrect username or password",
|
||||||
"group-id-with-value": "Group ID: {groupID}",
|
|
||||||
"group-name": "Group Name",
|
|
||||||
"groups": "Groups",
|
|
||||||
"groups-can-only-be-set-by-administrators": "Groups can only be set by administrators",
|
|
||||||
"link-id": "Link ID",
|
"link-id": "Link ID",
|
||||||
"link-name": "Link Name",
|
"link-name": "Link Name",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
@ -262,20 +354,29 @@
|
|||||||
"new-password": "New Password",
|
"new-password": "New Password",
|
||||||
"new-user": "New User",
|
"new-user": "New User",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
|
"password-has-been-reset-to-the-default-password": "Password has been reset to the default password",
|
||||||
"password-must-match": "Password must match",
|
"password-must-match": "Password must match",
|
||||||
|
"password-reset-failed": "Password reset failed",
|
||||||
|
"password-updated": "Password updated",
|
||||||
"reset-password": "Reset Password",
|
"reset-password": "Reset Password",
|
||||||
"sign-in": "Sign in",
|
"sign-in": "Sign in",
|
||||||
"sign-up-links": "Sign Up Links",
|
|
||||||
"total-mealplans": "Total MealPlans",
|
"total-mealplans": "Total MealPlans",
|
||||||
"total-users": "Total Users",
|
"total-users": "Total Users",
|
||||||
"upload-photo": "Upload Photo",
|
"upload-photo": "Upload Photo",
|
||||||
"use-8-characters-or-more-for-your-password": "Use 8 characters or more for your password",
|
"use-8-characters-or-more-for-your-password": "Use 8 characters or more for your password",
|
||||||
"user-group": "User Group",
|
"user-created": "User created",
|
||||||
|
"user-creation-failed": "User creation failed",
|
||||||
|
"user-deleted": "User deleted",
|
||||||
"user-id": "User ID",
|
"user-id": "User ID",
|
||||||
"user-id-with-value": "User ID: {id}",
|
"user-id-with-value": "User ID: {id}",
|
||||||
"user-password": "User Password",
|
"user-password": "User Password",
|
||||||
|
"user-successfully-logged-in": "User Successfully Logged In",
|
||||||
|
"user-update-failed": "User update failed",
|
||||||
|
"user-updated": "User updated",
|
||||||
"users": "Users",
|
"users": "Users",
|
||||||
"webhook-time": "Webhook Time",
|
"webhook-time": "Webhook Time",
|
||||||
"webhooks-enabled": "Webhooks Enabled"
|
"webhooks-enabled": "Webhooks Enabled",
|
||||||
|
"you-are-not-allowed-to-create-a-user": "You are not allowed to create a user",
|
||||||
|
"you-are-not-allowed-to-delete-this-user": "You are not allowed to delete this user"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -23,11 +23,11 @@
|
|||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<TheDownloadBtn
|
<TheDownloadBtn
|
||||||
button-text="Download Recipe JSON"
|
:button-text="$t('about.download-recipe-json')"
|
||||||
download-url="/api/debug/last-recipe-json"
|
download-url="/api/debug/last-recipe-json"
|
||||||
/>
|
/>
|
||||||
<TheDownloadBtn
|
<TheDownloadBtn
|
||||||
button-text="Download Log"
|
:button-text="$t('about.download-log')"
|
||||||
download-url="/api/debug/log"
|
download-url="/api/debug/log"
|
||||||
/>
|
/>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
|
@ -62,17 +62,21 @@ export default {
|
|||||||
},
|
},
|
||||||
async importBackup(data) {
|
async importBackup(data) {
|
||||||
this.$emit("loading");
|
this.$emit("loading");
|
||||||
let response = await api.backups.import(data.name, data);
|
const response = await api.backups.import(data.name, data);
|
||||||
|
if(response) {
|
||||||
let importData = response.data;
|
let importData = response.data;
|
||||||
|
|
||||||
this.$emit("finished", importData);
|
this.$emit("finished", importData);
|
||||||
|
} else {
|
||||||
|
this.$emit("finished");
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
deleteBackup(data) {
|
async deleteBackup(data) {
|
||||||
this.$emit("loading");
|
this.$emit("loading");
|
||||||
|
|
||||||
api.backups.delete(data.name);
|
if (await api.backups.delete(data.name)) {
|
||||||
this.selectedBackup = null;
|
this.selectedBackup = null;
|
||||||
|
}
|
||||||
this.backupLoading = false;
|
this.backupLoading = false;
|
||||||
|
|
||||||
this.$emit("finished");
|
this.$emit("finished");
|
||||||
|
@ -28,7 +28,7 @@ export default {
|
|||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
value: true,
|
value: true,
|
||||||
text: "Pages",
|
text: this.$t("settings.pages"),
|
||||||
},
|
},
|
||||||
themes: {
|
themes: {
|
||||||
value: true,
|
value: true,
|
||||||
|
@ -20,11 +20,11 @@
|
|||||||
<v-card-text class="mt-n4">
|
<v-card-text class="mt-n4">
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col sm="4">
|
<v-col sm="4">
|
||||||
<p>{{ $t("general.options") }}:</p>
|
<p>{{ $t("general.options") }}</p>
|
||||||
<ImportOptions @update-options="updateOptions" class="mt-5" />
|
<ImportOptions @update-options="updateOptions" class="mt-5" />
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col>
|
<v-col>
|
||||||
<p>{{ $t("general.templates") }}:</p>
|
<p>{{ $t("general.templates") }}</p>
|
||||||
<v-checkbox
|
<v-checkbox
|
||||||
v-for="template in availableTemplates"
|
v-for="template in availableTemplates"
|
||||||
:key="template"
|
:key="template"
|
||||||
@ -97,10 +97,11 @@ export default {
|
|||||||
templates: this.selectedTemplates,
|
templates: this.selectedTemplates,
|
||||||
};
|
};
|
||||||
|
|
||||||
await api.backups.create(data);
|
if (await api.backups.create(data)) {
|
||||||
|
this.$emit("created");
|
||||||
|
}
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
|
||||||
this.$emit("created");
|
|
||||||
},
|
},
|
||||||
appendTemplate(templateName) {
|
appendTemplate(templateName) {
|
||||||
if (this.selectedTemplates.includes(templateName)) {
|
if (this.selectedTemplates.includes(templateName)) {
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
<div>
|
<div>
|
||||||
<ConfirmationDialog
|
<ConfirmationDialog
|
||||||
ref="deleteGroupConfirm"
|
ref="deleteGroupConfirm"
|
||||||
:title="$t('user.confirm-group-deletion')"
|
:title="$t('group.confirm-group-deletion')"
|
||||||
:message="
|
:message="
|
||||||
$t('user.are-you-sure-you-want-to-delete-the-group', {
|
$t('group.are-you-sure-you-want-to-delete-the-group', {
|
||||||
groupName: group.name,
|
groupName: group.name,
|
||||||
})
|
})
|
||||||
"
|
"
|
||||||
@ -18,7 +18,7 @@
|
|||||||
<v-card-title class="py-1">{{ group.name }}</v-card-title>
|
<v-card-title class="py-1">{{ group.name }}</v-card-title>
|
||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
<v-subheader>{{
|
<v-subheader>{{
|
||||||
$t("user.group-id-with-value", { groupID: group.id })
|
$t("group.group-id-with-value", { groupID: group.id })
|
||||||
}}</v-subheader>
|
}}</v-subheader>
|
||||||
<v-list-item-group color="primary">
|
<v-list-item-group color="primary">
|
||||||
<v-list-item v-for="property in groupProps" :key="property.text">
|
<v-list-item v-for="property in groupProps" :key="property.text">
|
||||||
@ -91,8 +91,9 @@ export default {
|
|||||||
this.$refs.deleteGroupConfirm.open();
|
this.$refs.deleteGroupConfirm.open();
|
||||||
},
|
},
|
||||||
async deleteGroup() {
|
async deleteGroup() {
|
||||||
await api.groups.delete(this.group.id);
|
if (await api.groups.delete(this.group.id)) {
|
||||||
this.$emit(RENDER_EVENT);
|
this.$emit(RENDER_EVENT);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
closeGroupDelete() {
|
closeGroupDelete() {
|
||||||
console.log("Close Delete");
|
console.log("Close Delete");
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
v-on="on"
|
v-on="on"
|
||||||
>
|
>
|
||||||
{{ $t("user.create-group") }}
|
{{ $t("group.create-group") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<v-card>
|
<v-card>
|
||||||
@ -34,7 +34,7 @@
|
|||||||
</v-icon>
|
</v-icon>
|
||||||
|
|
||||||
<v-toolbar-title class="headline">
|
<v-toolbar-title class="headline">
|
||||||
{{ $t("user.create-group") }}
|
{{ $t("group.create-group") }}
|
||||||
</v-toolbar-title>
|
</v-toolbar-title>
|
||||||
|
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
@ -43,7 +43,7 @@
|
|||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="newGroupName"
|
v-model="newGroupName"
|
||||||
:label="$t('user.group-name')"
|
:label="$t('group.group-name')"
|
||||||
:rules="[existsRule]"
|
:rules="[existsRule]"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
@ -104,12 +104,11 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
async createGroup() {
|
async createGroup() {
|
||||||
this.groupLoading = true;
|
this.groupLoading = true;
|
||||||
let response = await api.groups.create(this.newGroupName);
|
if (await api.groups.create(this.newGroupName)) {
|
||||||
if (response.created) {
|
|
||||||
this.groupLoading = false;
|
|
||||||
this.groupDialog = false;
|
this.groupDialog = false;
|
||||||
this.$store.dispatch("requestAllGroups");
|
this.$store.dispatch("requestAllGroups");
|
||||||
}
|
}
|
||||||
|
this.groupLoading = false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-card outlined class="mt-n1">
|
<v-card outlined class="mt-n1">
|
||||||
<ConfirmationDialog
|
<ConfirmationDialog
|
||||||
ref="deleteUserDialog"
|
ref="deleteTokenDialog"
|
||||||
:title="$t('user.confirm-link-deletion')"
|
:title="$t('user.confirm-link-deletion')"
|
||||||
:message="
|
:message="
|
||||||
$t('user.are-you-sure-you-want-to-delete-the-link', {
|
$t('user.are-you-sure-you-want-to-delete-the-link', {
|
||||||
@ -9,7 +9,7 @@
|
|||||||
})
|
})
|
||||||
"
|
"
|
||||||
icon="mdi-alert"
|
icon="mdi-alert"
|
||||||
@confirm="deleteUser"
|
@confirm="deleteToken"
|
||||||
:width="450"
|
:width="450"
|
||||||
@close="closeDelete"
|
@close="closeDelete"
|
||||||
/>
|
/>
|
||||||
@ -18,7 +18,7 @@
|
|||||||
mdi-link-variant
|
mdi-link-variant
|
||||||
</v-icon>
|
</v-icon>
|
||||||
<v-toolbar-title class="headine">
|
<v-toolbar-title class="headine">
|
||||||
{{ $t("user.sign-up-links") }}
|
{{ $t("signup.sign-up-links") }}
|
||||||
</v-toolbar-title>
|
</v-toolbar-title>
|
||||||
|
|
||||||
<v-spacer> </v-spacer>
|
<v-spacer> </v-spacer>
|
||||||
@ -181,9 +181,10 @@ export default {
|
|||||||
this.links = await api.signUps.getAll();
|
this.links = await api.signUps.getAll();
|
||||||
},
|
},
|
||||||
|
|
||||||
async deleteUser() {
|
async deleteToken() {
|
||||||
await api.signUps.deleteToken(this.activeId);
|
if (await api.signUps.deleteToken(this.activeId)) {
|
||||||
this.initialize();
|
this.initialize();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
editItem(item) {
|
editItem(item) {
|
||||||
@ -197,7 +198,7 @@ export default {
|
|||||||
this.activeName = item.name;
|
this.activeName = item.name;
|
||||||
this.editedIndex = this.links.indexOf(item);
|
this.editedIndex = this.links.indexOf(item);
|
||||||
this.editedItem = Object.assign({}, item);
|
this.editedItem = Object.assign({}, item);
|
||||||
this.$refs.deleteUserDialog.open();
|
this.$refs.deleteTokenDialog.open();
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteItemConfirm() {
|
deleteItemConfirm() {
|
||||||
|
@ -72,7 +72,7 @@
|
|||||||
dense
|
dense
|
||||||
v-model="editedItem.group"
|
v-model="editedItem.group"
|
||||||
:items="existingGroups"
|
:items="existingGroups"
|
||||||
:label="$t('user.user-group')"
|
:label="$t('group.user-group')"
|
||||||
></v-select>
|
></v-select>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12" sm="12" md="6" v-if="showPassword">
|
<v-col cols="12" sm="12" md="6" v-if="showPassword">
|
||||||
@ -94,7 +94,7 @@
|
|||||||
|
|
||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<v-btn color="info" text @click="resetPassword">
|
<v-btn color="info" text @click="resetPassword">
|
||||||
Reset Password
|
{{$t('user.reset-password')}}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-btn color="grey" text @click="close">
|
<v-btn color="grey" text @click="close">
|
||||||
@ -165,7 +165,7 @@ export default {
|
|||||||
},
|
},
|
||||||
{ text: this.$t("user.full-name"), value: "fullName" },
|
{ text: this.$t("user.full-name"), value: "fullName" },
|
||||||
{ text: this.$t("user.email"), value: "email" },
|
{ text: this.$t("user.email"), value: "email" },
|
||||||
{ text: this.$t("user.group"), value: "group" },
|
{ text: this.$t("group.group"), value: "group" },
|
||||||
{ text: this.$t("user.admin"), value: "admin" },
|
{ text: this.$t("user.admin"), value: "admin" },
|
||||||
{ text: "", value: "actions", sortable: false, align: "center" },
|
{ text: "", value: "actions", sortable: false, align: "center" },
|
||||||
],
|
],
|
||||||
@ -223,8 +223,9 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async deleteUser() {
|
async deleteUser() {
|
||||||
await api.users.delete(this.activeId);
|
if (await api.users.delete(this.activeId)) {
|
||||||
this.initialize();
|
this.initialize();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
editItem(item) {
|
editItem(item) {
|
||||||
@ -264,17 +265,27 @@ export default {
|
|||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
if (this.editedIndex > -1) {
|
if (this.editedIndex > -1) {
|
||||||
await api.users.update(this.editedItem);
|
this.updateUser();
|
||||||
this.close();
|
|
||||||
} else if (this.$refs.newUser.validate()) {
|
} else if (this.$refs.newUser.validate()) {
|
||||||
await api.users.create(this.editedItem);
|
this.createUser();
|
||||||
this.close();
|
|
||||||
}
|
}
|
||||||
await this.initialize();
|
await this.initialize();
|
||||||
},
|
},
|
||||||
resetPassword() {
|
resetPassword() {
|
||||||
api.users.resetPassword(this.editedItem.id);
|
api.users.resetPassword(this.editedItem.id);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async createUser() {
|
||||||
|
if(await api.users.create(this.editedItem)) {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async updateUser() {
|
||||||
|
if(await api.users.update(this.editedItem)) {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -16,12 +16,12 @@
|
|||||||
</v-tab>
|
</v-tab>
|
||||||
|
|
||||||
<v-tab>
|
<v-tab>
|
||||||
{{ $t("user.sign-up-links") }}
|
{{ $t("signup.sign-up-links") }}
|
||||||
<v-icon>mdi-account-plus-outline</v-icon>
|
<v-icon>mdi-account-plus-outline</v-icon>
|
||||||
</v-tab>
|
</v-tab>
|
||||||
|
|
||||||
<v-tab>
|
<v-tab>
|
||||||
{{ $t("user.groups") }}
|
{{ $t("group.groups") }}
|
||||||
<v-icon>mdi-account-group</v-icon>
|
<v-icon>mdi-account-group</v-icon>
|
||||||
</v-tab>
|
</v-tab>
|
||||||
</v-tabs>
|
</v-tabs>
|
||||||
|
@ -135,9 +135,10 @@ export default {
|
|||||||
this.groupSettings.webhookUrls.splice(index, 1);
|
this.groupSettings.webhookUrls.splice(index, 1);
|
||||||
},
|
},
|
||||||
async saveGroupSettings() {
|
async saveGroupSettings() {
|
||||||
await api.groups.update(this.groupSettings);
|
if (await api.groups.update(this.groupSettings)) {
|
||||||
await this.$store.dispatch("requestCurrentGroup");
|
await this.$store.dispatch("requestCurrentGroup");
|
||||||
this.getSiteSettings();
|
this.getSiteSettings();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
testWebhooks() {
|
testWebhooks() {
|
||||||
api.settings.testWebhooks();
|
api.settings.testWebhooks();
|
||||||
|
@ -86,9 +86,10 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
deleteMigration(file_name) {
|
async deleteMigration(file_name) {
|
||||||
api.migrations.delete(this.folder, file_name);
|
if (await api.migrations.delete(this.folder, file_name)) {
|
||||||
this.$emit("refresh");
|
this.$emit("refresh");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async importMigration(file_name) {
|
async importMigration(file_name) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
@ -55,11 +55,11 @@
|
|||||||
>
|
>
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
:label="$t('user.group')"
|
:label="$t('group.group')"
|
||||||
readonly
|
readonly
|
||||||
v-model="user.group"
|
v-model="user.group"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
:hint="$t('user.groups-can-only-be-set-by-administrators')"
|
:hint="$t('group.groups-can-only-be-set-by-administrators')"
|
||||||
>
|
>
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
</v-form>
|
</v-form>
|
||||||
@ -201,11 +201,13 @@ export default {
|
|||||||
},
|
},
|
||||||
async updateUser() {
|
async updateUser() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
let newKey = await api.users.update(this.user);
|
const response = await api.users.update(this.user);
|
||||||
this.$store.commit("setToken", newKey.access_token);
|
if(response) {
|
||||||
|
this.$store.commit("setToken", response.data.access_token);
|
||||||
this.refreshProfile();
|
this.refreshProfile();
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.$store.dispatch("requestUserData");
|
this.$store.dispatch("requestUserData");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async changePassword() {
|
async changePassword() {
|
||||||
this.paswordLoading = true;
|
this.paswordLoading = true;
|
||||||
@ -215,7 +217,9 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (this.$refs.passChange.validate()) {
|
if (this.$refs.passChange.validate()) {
|
||||||
await api.users.changePassword(this.user.id, data);
|
if (await api.users.changePassword(this.user.id, data)) {
|
||||||
|
this.$emit("refresh");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.paswordLoading = false;
|
this.paswordLoading = false;
|
||||||
},
|
},
|
||||||
|
@ -82,14 +82,18 @@ export default {
|
|||||||
this.$refs.categoryFormSelector.setInit(this.page.categories);
|
this.$refs.categoryFormSelector.setInit(this.page.categories);
|
||||||
},
|
},
|
||||||
async submitForm() {
|
async submitForm() {
|
||||||
|
let response;
|
||||||
if (this.create) {
|
if (this.create) {
|
||||||
await api.siteSettings.createPage(this.page);
|
response = await api.siteSettings.createPage(this.page);
|
||||||
} else {
|
} else {
|
||||||
await api.siteSettings.updatePage(this.page);
|
response = await api.siteSettings.updatePage(this.page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (response) {
|
||||||
this.pageDialog = false;
|
this.pageDialog = false;
|
||||||
this.page.categories = [];
|
this.page.categories = [];
|
||||||
this.$emit(NEW_PAGE_EVENT);
|
this.$emit(NEW_PAGE_EVENT);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -109,9 +109,10 @@ export default {
|
|||||||
element.position = index;
|
element.position = index;
|
||||||
});
|
});
|
||||||
|
|
||||||
await api.siteSettings.updateAllPages(this.customPages);
|
if (await api.siteSettings.updateAllPages(this.customPages)) {
|
||||||
|
|
||||||
this.getPages();
|
this.getPages();
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
editPage(index) {
|
editPage(index) {
|
||||||
this.editPageData.data = this.customPages[index];
|
this.editPageData.data = this.customPages[index];
|
||||||
|
@ -223,8 +223,9 @@ export default {
|
|||||||
this.settings.categories.splice(index, 1);
|
this.settings.categories.splice(index, 1);
|
||||||
},
|
},
|
||||||
async saveSettings() {
|
async saveSettings() {
|
||||||
await api.siteSettings.update(this.settings);
|
if (await api.siteSettings.update(this.settings)) {
|
||||||
this.getOptions();
|
this.getOptions();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -70,11 +70,11 @@ export default {
|
|||||||
},
|
},
|
||||||
async deleteSelectedTheme() {
|
async deleteSelectedTheme() {
|
||||||
//Delete Theme from DB
|
//Delete Theme from DB
|
||||||
await api.themes.delete(this.theme.name);
|
if (await api.themes.delete(this.theme.name)) {
|
||||||
|
|
||||||
//Get the new list of available from DB
|
//Get the new list of available from DB
|
||||||
this.availableThemes = await api.themes.requestAll();
|
this.availableThemes = await api.themes.requestAll();
|
||||||
this.$emit(DELETE_EVENT);
|
this.$emit(DELETE_EVENT);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async saveThemes() {
|
async saveThemes() {
|
||||||
this.$store.commit("setTheme", this.theme);
|
this.$store.commit("setTheme", this.theme);
|
||||||
|
@ -171,9 +171,11 @@ export default {
|
|||||||
* Create the new Theme and select it.
|
* Create the new Theme and select it.
|
||||||
*/
|
*/
|
||||||
async appendTheme(NewThemeDialog) {
|
async appendTheme(NewThemeDialog) {
|
||||||
await api.themes.create(NewThemeDialog);
|
const response = await api.themes.create(NewThemeDialog);
|
||||||
|
if (response) {
|
||||||
this.availableThemes.push(NewThemeDialog);
|
this.availableThemes.push(NewThemeDialog);
|
||||||
this.$store.commit("setTheme", NewThemeDialog);
|
this.$store.commit("setTheme", NewThemeDialog);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
setStoresDarkMode() {
|
setStoresDarkMode() {
|
||||||
this.$store.commit("setDarkMode", this.selectedDarkMode);
|
this.$store.commit("setDarkMode", this.selectedDarkMode);
|
||||||
@ -181,8 +183,8 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* This will save the current colors and make the selected theme live.
|
* This will save the current colors and make the selected theme live.
|
||||||
*/
|
*/
|
||||||
async saveThemes() {
|
saveThemes() {
|
||||||
await api.themes.update(
|
api.themes.update(
|
||||||
this.selectedTheme.name,
|
this.selectedTheme.name,
|
||||||
this.selectedTheme.colors
|
this.selectedTheme.colors
|
||||||
);
|
);
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
ref="assignDialog"
|
ref="assignDialog"
|
||||||
title-icon="mdi-tag"
|
title-icon="mdi-tag"
|
||||||
color="primary"
|
color="primary"
|
||||||
title="Bulk Assign"
|
:title="$t('settings.toolbox.bulk-assign')"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
modal-width="700"
|
modal-width="700"
|
||||||
:top="true"
|
:top="true"
|
||||||
@ -13,7 +13,7 @@
|
|||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="search"
|
v-model="search"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
label="Keyword"
|
:label="$t('general.keyword')"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
<CategoryTagSelector
|
<CategoryTagSelector
|
||||||
:tag-selector="false"
|
:tag-selector="false"
|
||||||
@ -44,7 +44,7 @@
|
|||||||
<v-card-title class="headline"> </v-card-title>
|
<v-card-title class="headline"> </v-card-title>
|
||||||
<CardSection
|
<CardSection
|
||||||
class="px-2 pb-2"
|
class="px-2 pb-2"
|
||||||
:title="`${results.length || 0} Recipes Effected`"
|
:title="$tc('settings.toolbox.recipes-affected', results.length || 0)"
|
||||||
:mobile-cards="true"
|
:mobile-cards="true"
|
||||||
:recipes="results"
|
:recipes="results"
|
||||||
:single-column="true"
|
:single-column="true"
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
:title="
|
:title="
|
||||||
$t('general.delete') +
|
$t('general.delete') +
|
||||||
' ' +
|
' ' +
|
||||||
(isTags ? $t('recipe.tags') : $t('recipe.categories'))
|
(isTags ? $t('tag.tags') : $t('recipe.categories'))
|
||||||
"
|
"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
modal-width="400"
|
modal-width="400"
|
||||||
|
@ -18,8 +18,7 @@
|
|||||||
</v-form>
|
</v-form>
|
||||||
<template slot="below-actions">
|
<template slot="below-actions">
|
||||||
<v-card-title class="headline">
|
<v-card-title class="headline">
|
||||||
{{ renameTarget.recipes.length || 0 }}
|
{{ $tc("settings.toolbox.recipes-affected", renameTarget.recipes.length || 0) }}
|
||||||
{{ $t("settings.toolbox.recipes-effected") }}
|
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
<MobileRecipeCard
|
<MobileRecipeCard
|
||||||
class="ml-2 mr-2 mt-2 mb-2"
|
class="ml-2 mr-2 mt-2 mb-2"
|
||||||
@ -94,10 +93,10 @@
|
|||||||
<v-card-title class="py-1">{{ item.name }}</v-card-title>
|
<v-card-title class="py-1">{{ item.name }}</v-card-title>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-btn small text color="info" @click="openEditDialog(item)">
|
<v-btn small text color="info" @click="openEditDialog(item)">
|
||||||
Edit
|
{{$t('general.edit')}}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn small text color="error" @click="deleteItem(item.slug)"
|
<v-btn small text color="error" @click="deleteItem(item.slug)">
|
||||||
>Delete
|
{{$t('general.delete')}}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
</v-card>
|
</v-card>
|
||||||
@ -177,7 +176,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.renameTarget = {
|
this.renameTarget = {
|
||||||
title: `Rename ${item.name}`,
|
title:this.$t('general.rename-object', [item.name]),
|
||||||
name: item.name,
|
name: item.name,
|
||||||
slug: item.slug,
|
slug: item.slug,
|
||||||
newName: "",
|
newName: "",
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
</v-tab>
|
</v-tab>
|
||||||
|
|
||||||
<v-tab>
|
<v-tab>
|
||||||
{{ $t("recipe.tags") }}
|
{{ $t("tag.tags") }}
|
||||||
<v-icon>mdi-tag-multiple-outline</v-icon>
|
<v-icon>mdi-tag-multiple-outline</v-icon>
|
||||||
</v-tab>
|
</v-tab>
|
||||||
</v-tabs>
|
</v-tabs>
|
||||||
|
@ -128,8 +128,9 @@ export default {
|
|||||||
this.requestMeals();
|
this.requestMeals();
|
||||||
},
|
},
|
||||||
async deletePlan(id) {
|
async deletePlan(id) {
|
||||||
await api.mealPlans.delete(id);
|
if (await api.mealPlans.delete(id)) {
|
||||||
this.requestMeals();
|
this.requestMeals();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
openShoppingList(id) {
|
openShoppingList(id) {
|
||||||
this.$refs.shoppingList.openDialog(id);
|
this.$refs.shoppingList.openDialog(id);
|
||||||
|
@ -52,6 +52,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { api } from "@/api";
|
import { api } from "@/api";
|
||||||
|
import utils from "@/utils";
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -60,6 +61,9 @@ export default {
|
|||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
this.mealPlan = await api.mealPlans.thisWeek();
|
this.mealPlan = await api.mealPlans.thisWeek();
|
||||||
|
if(!this.mealPlan) {
|
||||||
|
utils.notify.warning(this.$t('meal-plan.no-meal-plan-defined-yet'))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getOrder(index) {
|
getOrder(index) {
|
||||||
|
@ -101,7 +101,7 @@ export default {
|
|||||||
let slug = await api.recipes.create(this.recipeDetails);
|
let slug = await api.recipes.create(this.recipeDetails);
|
||||||
|
|
||||||
if (this.fileObject) {
|
if (this.fileObject) {
|
||||||
await api.recipes.updateImage(slug, this.fileObject);
|
api.recipes.updateImage(slug, this.fileObject, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
|
@ -80,6 +80,8 @@ import RecipeEditor from "@/components/Recipe/RecipeEditor";
|
|||||||
import RecipeTimeCard from "@/components/Recipe/RecipeTimeCard.vue";
|
import RecipeTimeCard from "@/components/Recipe/RecipeTimeCard.vue";
|
||||||
import EditorButtonRow from "@/components/Recipe/EditorButtonRow";
|
import EditorButtonRow from "@/components/Recipe/EditorButtonRow";
|
||||||
import { user } from "@/mixins/user";
|
import { user } from "@/mixins/user";
|
||||||
|
import store from "@/store";
|
||||||
|
import { router } from "@/routes";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -166,8 +168,12 @@ export default {
|
|||||||
return api.recipes.recipeImage(image) + "&rnd=" + this.imageKey;
|
return api.recipes.recipeImage(image) + "&rnd=" + this.imageKey;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
deleteRecipe() {
|
async deleteRecipe() {
|
||||||
api.recipes.delete(this.recipeDetails.slug);
|
let response = await api.recipes.delete(this.recipeDetails.slug);
|
||||||
|
if (response) {
|
||||||
|
store.dispatch("requestRecentRecipes");
|
||||||
|
router.push(`/`);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
validateRecipe() {
|
validateRecipe() {
|
||||||
if (this.jsonEditor) {
|
if (this.jsonEditor) {
|
||||||
@ -176,18 +182,19 @@ export default {
|
|||||||
return this.$refs.recipeEditor.validateRecipe();
|
return this.$refs.recipeEditor.validateRecipe();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async saveImage() {
|
async saveImage(overrideSuccessMsg = false) {
|
||||||
if (this.fileObject) {
|
if (this.fileObject) {
|
||||||
await api.recipes.updateImage(this.recipeDetails.slug, this.fileObject);
|
if (api.recipes.updateImage(this.recipeDetails.slug, this.fileObject, overrideSuccessMsg)) {
|
||||||
}
|
|
||||||
this.imageKey += 1;
|
this.imageKey += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async saveRecipe() {
|
async saveRecipe() {
|
||||||
if (this.validateRecipe()) {
|
if (this.validateRecipe()) {
|
||||||
let slug = await api.recipes.update(this.recipeDetails);
|
let slug = await api.recipes.update(this.recipeDetails);
|
||||||
|
|
||||||
if (this.fileObject) {
|
if (this.fileObject) {
|
||||||
this.saveImage();
|
this.saveImage(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.form = false;
|
this.form = false;
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
<v-row dense class="mt-0 flex-row align-center justify-space-around">
|
<v-row dense class="mt-0 flex-row align-center justify-space-around">
|
||||||
<v-col>
|
<v-col>
|
||||||
<h3 class="pl-2 text-center headline">
|
<h3 class="pl-2 text-center headline">
|
||||||
{{ $t("search.category-filter") }}
|
{{ $t("category.category-filter") }}
|
||||||
</h3>
|
</h3>
|
||||||
<FilterSelector class="mb-1" @update="updateCatParams" />
|
<FilterSelector class="mb-1" @update="updateCatParams" />
|
||||||
<CategoryTagSelector
|
<CategoryTagSelector
|
||||||
|
@ -2,6 +2,9 @@ import Planner from "@/pages/MealPlan/Planner";
|
|||||||
import ThisWeek from "@/pages/MealPlan/ThisWeek";
|
import ThisWeek from "@/pages/MealPlan/ThisWeek";
|
||||||
import { api } from "@/api";
|
import { api } from "@/api";
|
||||||
|
|
||||||
|
import i18n from '@/i18n.js';
|
||||||
|
import utils from "@/utils";
|
||||||
|
|
||||||
export const mealRoutes = [
|
export const mealRoutes = [
|
||||||
{
|
{
|
||||||
path: "/meal-plan/planner",
|
path: "/meal-plan/planner",
|
||||||
@ -21,7 +24,12 @@ export const mealRoutes = [
|
|||||||
path: "/meal-plan/today",
|
path: "/meal-plan/today",
|
||||||
beforeEnter: async (_to, _from, next) => {
|
beforeEnter: async (_to, _from, next) => {
|
||||||
await todaysMealRoute().then(redirect => {
|
await todaysMealRoute().then(redirect => {
|
||||||
|
if(redirect) {
|
||||||
next(redirect);
|
next(redirect);
|
||||||
|
} else {
|
||||||
|
utils.notify.error(i18n.t('meal-plan.no-meal-planned-for-today'));
|
||||||
|
next(_from);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -29,5 +37,9 @@ export const mealRoutes = [
|
|||||||
|
|
||||||
async function todaysMealRoute() {
|
async function todaysMealRoute() {
|
||||||
const response = await api.mealPlans.today();
|
const response = await api.mealPlans.today();
|
||||||
|
if (response.status == 200 && response.data) {
|
||||||
return "/recipe/" + response.data;
|
return "/recipe/" + response.data;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,12 @@ import operator
|
|||||||
import shutil
|
import shutil
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
|
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
|
||||||
from mealie.core.config import app_dirs
|
from mealie.core.config import app_dirs
|
||||||
from mealie.core.security import create_file_token
|
from mealie.core.security import create_file_token
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user, validate_file_token
|
from mealie.routes.deps import get_current_user, validate_file_token
|
||||||
from mealie.schema.backup import BackupJob, ImportJob, Imports, LocalBackup
|
from mealie.schema.backup import BackupJob, ImportJob, Imports, LocalBackup
|
||||||
from mealie.schema.snackbar import SnackResponse
|
|
||||||
from mealie.services.backups import imports
|
from mealie.services.backups import imports
|
||||||
from mealie.services.backups.exports import backup_all
|
from mealie.services.backups.exports import backup_all
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
@ -31,9 +30,10 @@ def available_imports():
|
|||||||
return Imports(imports=imports, templates=templates)
|
return Imports(imports=imports, templates=templates)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/export/database", status_code=201)
|
@router.post("/export/database", status_code=status.HTTP_201_CREATED)
|
||||||
def export_database(data: BackupJob, session: Session = Depends(generate_session)):
|
def export_database(data: BackupJob, session: Session = Depends(generate_session)):
|
||||||
"""Generates a backup of the recipe database in json format."""
|
"""Generates a backup of the recipe database in json format."""
|
||||||
|
try:
|
||||||
export_path = backup_all(
|
export_path = backup_all(
|
||||||
session=session,
|
session=session,
|
||||||
tag=data.tag,
|
tag=data.tag,
|
||||||
@ -45,16 +45,12 @@ def export_database(data: BackupJob, session: Session = Depends(generate_session
|
|||||||
export_users=data.options.users,
|
export_users=data.options.users,
|
||||||
export_groups=data.options.groups,
|
export_groups=data.options.groups,
|
||||||
)
|
)
|
||||||
try:
|
return {"export_path": export_path}
|
||||||
return SnackResponse.success("Backup Created at " + export_path)
|
|
||||||
except:
|
except:
|
||||||
HTTPException(
|
raise HTTPException( status.HTTP_500_INTERNAL_SERVER_ERROR )
|
||||||
status_code=400,
|
|
||||||
detail=SnackResponse.error("Error Creating Backup. See Log File"),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/upload")
|
@router.post("/upload", status_code=status.HTTP_200_OK)
|
||||||
def upload_backup_file(archive: UploadFile = File(...)):
|
def upload_backup_file(archive: UploadFile = File(...)):
|
||||||
""" Upload a .zip File to later be imported into Mealie """
|
""" Upload a .zip File to later be imported into Mealie """
|
||||||
dest = app_dirs.BACKUP_DIR.joinpath(archive.filename)
|
dest = app_dirs.BACKUP_DIR.joinpath(archive.filename)
|
||||||
@ -62,10 +58,9 @@ def upload_backup_file(archive: UploadFile = File(...)):
|
|||||||
with dest.open("wb") as buffer:
|
with dest.open("wb") as buffer:
|
||||||
shutil.copyfileobj(archive.file, buffer)
|
shutil.copyfileobj(archive.file, buffer)
|
||||||
|
|
||||||
if dest.is_file:
|
if not dest.is_file:
|
||||||
return SnackResponse.success("Backup uploaded")
|
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||||
else:
|
|
||||||
return SnackResponse.error("Failure uploading file")
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{file_name}/download")
|
@router.get("/{file_name}/download")
|
||||||
@ -76,7 +71,7 @@ async def download_backup_file(file_name: str):
|
|||||||
return {"fileToken": create_file_token(file)}
|
return {"fileToken": create_file_token(file)}
|
||||||
|
|
||||||
|
|
||||||
@router.post("/{file_name}/import", status_code=200)
|
@router.post("/{file_name}/import", status_code=status.HTTP_200_OK)
|
||||||
def import_database(file_name: str, import_data: ImportJob, session: Session = Depends(generate_session)):
|
def import_database(file_name: str, import_data: ImportJob, session: Session = Depends(generate_session)):
|
||||||
""" Import a database backup file generated from Mealie. """
|
""" Import a database backup file generated from Mealie. """
|
||||||
|
|
||||||
@ -94,16 +89,14 @@ def import_database(file_name: str, import_data: ImportJob, session: Session = D
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{file_name}/delete", status_code=200)
|
@router.delete("/{file_name}/delete", status_code=status.HTTP_200_OK)
|
||||||
def delete_backup(file_name: str):
|
def delete_backup(file_name: str):
|
||||||
""" Removes a database backup from the file system """
|
""" Removes a database backup from the file system """
|
||||||
|
file_path = app_dirs.BACKUP_DIR.joinpath(file_name)
|
||||||
|
|
||||||
|
if not file_path.is_file():
|
||||||
|
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||||
try:
|
try:
|
||||||
app_dirs.BACKUP_DIR.joinpath(file_name).unlink()
|
file_path.unlink()
|
||||||
except:
|
except:
|
||||||
HTTPException(
|
raise HTTPException( status.HTTP_500_INTERNAL_SERVER_ERROR )
|
||||||
status_code=400,
|
|
||||||
detail=SnackResponse.error("Unable to Delete Backup. See Log File"),
|
|
||||||
)
|
|
||||||
|
|
||||||
return SnackResponse.error(f"{file_name} Deleted")
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends, status, HTTPException
|
||||||
from mealie.db.database import db
|
from mealie.db.database import db
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
from mealie.schema.snackbar import SnackResponse
|
|
||||||
from mealie.schema.user import GroupBase, GroupInDB, UpdateGroup, UserInDB
|
from mealie.schema.user import GroupBase, GroupInDB, UpdateGroup, UserInDB
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
@ -30,7 +29,7 @@ async def get_current_user_group(
|
|||||||
return db.groups.get(session, current_user.group, "name")
|
return db.groups.get(session, current_user.group, "name")
|
||||||
|
|
||||||
|
|
||||||
@router.post("")
|
@router.post("", status_code=status.HTTP_201_CREATED)
|
||||||
async def create_group(
|
async def create_group(
|
||||||
group_data: GroupBase,
|
group_data: GroupBase,
|
||||||
current_user=Depends(get_current_user),
|
current_user=Depends(get_current_user),
|
||||||
@ -40,9 +39,8 @@ async def create_group(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
db.groups.create(session, group_data.dict())
|
db.groups.create(session, group_data.dict())
|
||||||
return SnackResponse.success("User Group Created", {"created": True})
|
|
||||||
except:
|
except:
|
||||||
return SnackResponse.error("User Group Creation Failed")
|
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{id}")
|
@router.put("/{id}")
|
||||||
@ -55,8 +53,6 @@ async def update_group_data(
|
|||||||
""" Updates a User Group """
|
""" Updates a User Group """
|
||||||
db.groups.update(session, id, group_data.dict())
|
db.groups.update(session, id, group_data.dict())
|
||||||
|
|
||||||
return SnackResponse.success("Group Settings Updated")
|
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{id}")
|
@router.delete("/{id}")
|
||||||
async def delete_user_group(
|
async def delete_user_group(
|
||||||
@ -65,16 +61,23 @@ async def delete_user_group(
|
|||||||
""" Removes a user group from the database """
|
""" Removes a user group from the database """
|
||||||
|
|
||||||
if id == 1:
|
if id == 1:
|
||||||
return SnackResponse.error("Cannot delete default group")
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail='DEFAULT_GROUP'
|
||||||
|
)
|
||||||
|
|
||||||
group: GroupInDB = db.groups.get(session, id)
|
group: GroupInDB = db.groups.get(session, id)
|
||||||
|
|
||||||
if not group:
|
if not group:
|
||||||
return SnackResponse.error("Group not found")
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail='GROUP_NOT_FOUND'
|
||||||
|
)
|
||||||
|
|
||||||
if not group.users == []:
|
if not group.users == []:
|
||||||
return SnackResponse.error("Cannot delete group with users")
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail='GROUP_WITH_USERS'
|
||||||
|
)
|
||||||
|
|
||||||
db.groups.delete(session, id)
|
db.groups.delete(session, id)
|
||||||
|
|
||||||
return
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from mealie.db.database import db
|
from mealie.db.database import db
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
from mealie.schema.meal import MealPlanIn, MealPlanInDB
|
from mealie.schema.meal import MealPlanIn, MealPlanInDB
|
||||||
from mealie.schema.snackbar import SnackResponse
|
|
||||||
from mealie.schema.user import GroupInDB, UserInDB
|
from mealie.schema.user import GroupInDB, UserInDB
|
||||||
from mealie.services.image import image
|
from mealie.services.image import image
|
||||||
from mealie.services.meal_services import get_todays_meal, process_meals
|
from mealie.services.meal_services import get_todays_meal, process_meals
|
||||||
@ -23,15 +22,13 @@ def get_all_meals(
|
|||||||
return db.groups.get_meals(session, current_user.group)
|
return db.groups.get_meals(session, current_user.group)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/create")
|
@router.post("/create", status_code=status.HTTP_201_CREATED)
|
||||||
def create_meal_plan(
|
def create_meal_plan(
|
||||||
data: MealPlanIn, session: Session = Depends(generate_session), current_user=Depends(get_current_user)
|
data: MealPlanIn, session: Session = Depends(generate_session), current_user=Depends(get_current_user)
|
||||||
):
|
):
|
||||||
""" Creates a meal plan database entry """
|
""" Creates a meal plan database entry """
|
||||||
processed_plan = process_meals(session, data)
|
processed_plan = process_meals(session, data)
|
||||||
db.meals.create(session, processed_plan.dict())
|
return db.meals.create(session, processed_plan.dict())
|
||||||
|
|
||||||
return SnackResponse.success("Mealplan Created")
|
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{plan_id}")
|
@router.put("/{plan_id}")
|
||||||
@ -44,25 +41,28 @@ def update_meal_plan(
|
|||||||
""" Updates a meal plan based off ID """
|
""" Updates a meal plan based off ID """
|
||||||
processed_plan = process_meals(session, meal_plan)
|
processed_plan = process_meals(session, meal_plan)
|
||||||
processed_plan = MealPlanInDB(uid=plan_id, **processed_plan.dict())
|
processed_plan = MealPlanInDB(uid=plan_id, **processed_plan.dict())
|
||||||
|
try:
|
||||||
db.meals.update(session, plan_id, processed_plan.dict())
|
db.meals.update(session, plan_id, processed_plan.dict())
|
||||||
|
except:
|
||||||
return SnackResponse.info("Mealplan Updated")
|
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{plan_id}")
|
@router.delete("/{plan_id}")
|
||||||
def delete_meal_plan(plan_id, session: Session = Depends(generate_session), current_user=Depends(get_current_user)):
|
def delete_meal_plan(plan_id, session: Session = Depends(generate_session), current_user=Depends(get_current_user)):
|
||||||
""" Removes a meal plan from the database """
|
""" Removes a meal plan from the database """
|
||||||
|
|
||||||
|
try:
|
||||||
db.meals.delete(session, plan_id)
|
db.meals.delete(session, plan_id)
|
||||||
|
except:
|
||||||
return SnackResponse.error("Mealplan Deleted")
|
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||||
|
|
||||||
|
|
||||||
@router.get("/this-week", response_model=MealPlanInDB)
|
@router.get("/this-week", response_model=MealPlanInDB)
|
||||||
def get_this_week(session: Session = Depends(generate_session), current_user: UserInDB = Depends(get_current_user)):
|
def get_this_week(session: Session = Depends(generate_session), current_user: UserInDB = Depends(get_current_user)):
|
||||||
""" Returns the meal plan data for this week """
|
""" Returns the meal plan data for this week """
|
||||||
|
plans = db.groups.get_meals(session, current_user.group)
|
||||||
return db.groups.get_meals(session, current_user.group)[0]
|
if plans:
|
||||||
|
return plans[0]
|
||||||
|
|
||||||
|
|
||||||
@router.get("/today", tags=["Meal Plan"])
|
@router.get("/today", tags=["Meal Plan"])
|
||||||
@ -74,7 +74,7 @@ def get_today(session: Session = Depends(generate_session), current_user: UserIn
|
|||||||
|
|
||||||
group_in_db: GroupInDB = db.groups.get(session, current_user.group, "name")
|
group_in_db: GroupInDB = db.groups.get(session, current_user.group, "name")
|
||||||
recipe = get_todays_meal(session, group_in_db)
|
recipe = get_todays_meal(session, group_in_db)
|
||||||
|
if recipe:
|
||||||
return recipe.slug
|
return recipe.slug
|
||||||
|
|
||||||
|
|
||||||
@ -90,8 +90,8 @@ def get_todays_image(session: Session = Depends(generate_session), group_name: s
|
|||||||
if recipe:
|
if recipe:
|
||||||
recipe_image = image.read_image(recipe.slug, image_type=image.IMG_OPTIONS.ORIGINAL_IMAGE)
|
recipe_image = image.read_image(recipe.slug, image_type=image.IMG_OPTIONS.ORIGINAL_IMAGE)
|
||||||
else:
|
else:
|
||||||
raise HTTPException(404, "no meal for today")
|
raise HTTPException( status.HTTP_404_NOT_FOUND )
|
||||||
if recipe_image:
|
if recipe_image:
|
||||||
return FileResponse(recipe_image)
|
return FileResponse(recipe_image)
|
||||||
else:
|
else:
|
||||||
raise HTTPException(404, "file not found")
|
raise HTTPException( status.HTTP_404_NOT_FOUND )
|
||||||
|
@ -2,12 +2,11 @@ import operator
|
|||||||
import shutil
|
import shutil
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, File, UploadFile
|
from fastapi import APIRouter, Depends, File, UploadFile, status
|
||||||
from mealie.core.config import app_dirs
|
from mealie.core.config import app_dirs
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
from mealie.schema.migration import MigrationFile, Migrations
|
from mealie.schema.migration import MigrationFile, Migrations
|
||||||
from mealie.schema.snackbar import SnackResponse
|
|
||||||
from mealie.services.migrations import migration
|
from mealie.services.migrations import migration
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
@ -42,7 +41,7 @@ def import_migration(import_type: migration.Migration, file_name: str, session:
|
|||||||
return migration.migrate(import_type, file_path, session)
|
return migration.migrate(import_type, file_path, session)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{import_type}/{file_name}/delete")
|
@router.delete("/{import_type}/{file_name}/delete", status_code=status.HTTP_200_OK)
|
||||||
def delete_migration_data(import_type: migration.Migration, file_name: str):
|
def delete_migration_data(import_type: migration.Migration, file_name: str):
|
||||||
""" Removes migration data from the file system """
|
""" Removes migration data from the file system """
|
||||||
|
|
||||||
@ -53,12 +52,11 @@ def delete_migration_data(import_type: migration.Migration, file_name: str):
|
|||||||
elif remove_path.is_dir():
|
elif remove_path.is_dir():
|
||||||
shutil.rmtree(remove_path)
|
shutil.rmtree(remove_path)
|
||||||
else:
|
else:
|
||||||
SnackResponse.error("File/Folder not found.")
|
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||||
|
|
||||||
return SnackResponse.error(f"Migration Data Remove: {remove_path.absolute()}")
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/{import_type}/upload")
|
|
||||||
|
@router.post("/{import_type}/upload", status_code=status.HTTP_200_OK)
|
||||||
def upload_nextcloud_zipfile(import_type: migration.Migration, archive: UploadFile = File(...)):
|
def upload_nextcloud_zipfile(import_type: migration.Migration, archive: UploadFile = File(...)):
|
||||||
""" Upload a .zip File to later be imported into Mealie """
|
""" Upload a .zip File to later be imported into Mealie """
|
||||||
dir = app_dirs.MIGRATION_DIR.joinpath(import_type.value)
|
dir = app_dirs.MIGRATION_DIR.joinpath(import_type.value)
|
||||||
@ -68,7 +66,5 @@ def upload_nextcloud_zipfile(import_type: migration.Migration, archive: UploadFi
|
|||||||
with dest.open("wb") as buffer:
|
with dest.open("wb") as buffer:
|
||||||
shutil.copyfileobj(archive.file, buffer)
|
shutil.copyfileobj(archive.file, buffer)
|
||||||
|
|
||||||
if dest.is_file:
|
if not dest.is_file:
|
||||||
return SnackResponse.success("Migration data uploaded")
|
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||||
else:
|
|
||||||
return SnackResponse.error("Failure uploading file")
|
|
@ -1,9 +1,8 @@
|
|||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from mealie.db.database import db
|
from mealie.db.database import db
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
from mealie.schema.category import CategoryIn, RecipeCategoryResponse
|
from mealie.schema.category import CategoryIn, RecipeCategoryResponse
|
||||||
from mealie.schema.snackbar import SnackResponse
|
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
@ -36,7 +35,10 @@ async def create_recipe_category(
|
|||||||
):
|
):
|
||||||
""" Creates a Category in the database """
|
""" Creates a Category in the database """
|
||||||
|
|
||||||
|
try:
|
||||||
return db.categories.create(session, category.dict())
|
return db.categories.create(session, category.dict())
|
||||||
|
except:
|
||||||
|
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{category}", response_model=RecipeCategoryResponse)
|
@router.put("/{category}", response_model=RecipeCategoryResponse)
|
||||||
@ -48,7 +50,10 @@ async def update_recipe_category(
|
|||||||
):
|
):
|
||||||
""" Updates an existing Tag in the database """
|
""" Updates an existing Tag in the database """
|
||||||
|
|
||||||
|
try:
|
||||||
return db.categories.update(session, category, new_category.dict())
|
return db.categories.update(session, category, new_category.dict())
|
||||||
|
except:
|
||||||
|
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{category}")
|
@router.delete("/{category}")
|
||||||
@ -59,6 +64,7 @@ async def delete_recipe_category(
|
|||||||
category does not impact a recipe. The category will be removed
|
category does not impact a recipe. The category will be removed
|
||||||
from any recipes that contain it"""
|
from any recipes that contain it"""
|
||||||
|
|
||||||
|
try:
|
||||||
db.categories.delete(session, category)
|
db.categories.delete(session, category)
|
||||||
|
except:
|
||||||
return SnackResponse.error(f"Category Deleted: {category}")
|
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, File, Form
|
from fastapi import APIRouter, Depends, File, Form, status, HTTPException
|
||||||
from fastapi.datastructures import UploadFile
|
from fastapi.datastructures import UploadFile
|
||||||
from mealie.core.config import app_dirs
|
from mealie.core.config import app_dirs
|
||||||
from mealie.db.database import db
|
from mealie.db.database import db
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
from mealie.schema.recipe import Recipe, RecipeAsset
|
from mealie.schema.recipe import Recipe, RecipeAsset
|
||||||
from mealie.schema.snackbar import SnackResponse
|
|
||||||
from slugify import slugify
|
from slugify import slugify
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
from starlette.responses import FileResponse
|
from starlette.responses import FileResponse
|
||||||
@ -41,10 +40,10 @@ def upload_recipe_asset(
|
|||||||
with dest.open("wb") as buffer:
|
with dest.open("wb") as buffer:
|
||||||
shutil.copyfileobj(file.file, buffer)
|
shutil.copyfileobj(file.file, buffer)
|
||||||
|
|
||||||
if dest.is_file():
|
if not dest.is_file():
|
||||||
|
raise HTTPException( status.HTTP_500_INTERNAL_SERVER_ERROR )
|
||||||
|
|
||||||
recipe: Recipe = db.recipes.get(session, recipe_slug)
|
recipe: Recipe = db.recipes.get(session, recipe_slug)
|
||||||
recipe.assets.append(asset_in)
|
recipe.assets.append(asset_in)
|
||||||
db.recipes.update(session, recipe_slug, recipe.dict())
|
db.recipes.update(session, recipe_slug, recipe.dict())
|
||||||
return asset_in
|
return asset_in
|
||||||
else:
|
|
||||||
return SnackResponse.error("Failure uploading file")
|
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, File, Form, HTTPException
|
from fastapi import APIRouter, Depends, File, Form, HTTPException, status
|
||||||
from fastapi.responses import FileResponse
|
from fastapi.responses import FileResponse
|
||||||
from mealie.db.database import db
|
from mealie.db.database import db
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
from mealie.schema.recipe import Recipe, RecipeURLIn
|
from mealie.schema.recipe import Recipe, RecipeURLIn
|
||||||
from mealie.schema.snackbar import SnackResponse
|
|
||||||
from mealie.services.image.image import IMG_OPTIONS, delete_image, read_image, rename_image, scrape_image, write_image
|
from mealie.services.image.image import IMG_OPTIONS, delete_image, read_image, rename_image, scrape_image, write_image
|
||||||
from mealie.services.scraper.scraper import create_from_url
|
from mealie.services.scraper.scraper import create_from_url
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
@ -100,9 +99,8 @@ def delete_recipe(
|
|||||||
db.recipes.delete(session, recipe_slug)
|
db.recipes.delete(session, recipe_slug)
|
||||||
delete_image(recipe_slug)
|
delete_image(recipe_slug)
|
||||||
except:
|
except:
|
||||||
raise HTTPException(status_code=404, detail=SnackResponse.error("Unable to Delete Recipe"))
|
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||||
|
|
||||||
return SnackResponse.error(f"Recipe {recipe_slug} Deleted")
|
|
||||||
|
|
||||||
|
|
||||||
class ImageType(str, Enum):
|
class ImageType(str, Enum):
|
||||||
@ -125,7 +123,7 @@ async def get_recipe_img(recipe_slug: str, image_type: ImageType = ImageType.ori
|
|||||||
if recipe_image:
|
if recipe_image:
|
||||||
return FileResponse(recipe_image)
|
return FileResponse(recipe_image)
|
||||||
else:
|
else:
|
||||||
raise HTTPException(404, "file not found")
|
raise HTTPException( status.HTTP_404_NOT_FOUND )
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{recipe_slug}/image")
|
@router.put("/{recipe_slug}/image")
|
||||||
@ -152,5 +150,3 @@ def scrape_image_url(
|
|||||||
""" Removes an existing image and replaces it with the incoming file. """
|
""" Removes an existing image and replaces it with the incoming file. """
|
||||||
|
|
||||||
scrape_image(url.url, recipe_slug)
|
scrape_image(url.url, recipe_slug)
|
||||||
|
|
||||||
return SnackResponse.success("Recipe Image Updated")
|
|
||||||
|
@ -3,7 +3,6 @@ from mealie.db.database import db
|
|||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
from mealie.schema.category import RecipeTagResponse, TagIn
|
from mealie.schema.category import RecipeTagResponse, TagIn
|
||||||
from mealie.schema.snackbar import SnackResponse
|
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
router = APIRouter(tags=["Recipes"])
|
router = APIRouter(tags=["Recipes"])
|
||||||
@ -58,6 +57,7 @@ async def delete_recipe_tag(
|
|||||||
tag does not impact a recipe. The tag will be removed
|
tag does not impact a recipe. The tag will be removed
|
||||||
from any recipes that contain it"""
|
from any recipes that contain it"""
|
||||||
|
|
||||||
|
try:
|
||||||
db.tags.delete(session, tag)
|
db.tags.delete(session, tag)
|
||||||
|
except:
|
||||||
return SnackResponse.error(f"Tag Deleted: {tag}")
|
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||||
|
@ -5,7 +5,6 @@ from mealie.db.database import db
|
|||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
from mealie.schema.settings import CustomPageBase, CustomPageOut
|
from mealie.schema.settings import CustomPageBase, CustomPageOut
|
||||||
from mealie.schema.snackbar import SnackResponse
|
|
||||||
from mealie.schema.user import UserInDB
|
from mealie.schema.user import UserInDB
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
@ -29,8 +28,6 @@ async def create_new_page(
|
|||||||
|
|
||||||
db.custom_pages.create(session, new_page.dict())
|
db.custom_pages.create(session, new_page.dict())
|
||||||
|
|
||||||
return SnackResponse.success("New Page Created")
|
|
||||||
|
|
||||||
|
|
||||||
@router.put("")
|
@router.put("")
|
||||||
async def update_multiple_pages(
|
async def update_multiple_pages(
|
||||||
@ -41,7 +38,6 @@ async def update_multiple_pages(
|
|||||||
""" Update multiple custom pages """
|
""" Update multiple custom pages """
|
||||||
for page in pages:
|
for page in pages:
|
||||||
db.custom_pages.update(session, page.id, page.dict())
|
db.custom_pages.update(session, page.id, page.dict())
|
||||||
return SnackResponse.success("Pages Updated")
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{id}")
|
@router.get("/{id}")
|
||||||
@ -57,7 +53,7 @@ async def get_single_page(
|
|||||||
|
|
||||||
|
|
||||||
@router.put("/{id}")
|
@router.put("/{id}")
|
||||||
async def update_single_age(
|
async def update_single_page(
|
||||||
data: CustomPageOut,
|
data: CustomPageOut,
|
||||||
id: int,
|
id: int,
|
||||||
session: Session = Depends(generate_session),
|
session: Session = Depends(generate_session),
|
||||||
|
@ -3,7 +3,6 @@ from mealie.db.database import db
|
|||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
from mealie.schema.settings import SiteSettings
|
from mealie.schema.settings import SiteSettings
|
||||||
from mealie.schema.snackbar import SnackResponse
|
|
||||||
from mealie.schema.user import GroupInDB, UserInDB
|
from mealie.schema.user import GroupInDB, UserInDB
|
||||||
from mealie.utils.post_webhooks import post_webhooks
|
from mealie.utils.post_webhooks import post_webhooks
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
@ -27,8 +26,6 @@ def update_settings(
|
|||||||
""" Returns Site Settings """
|
""" Returns Site Settings """
|
||||||
db.settings.update(session, 1, data.dict())
|
db.settings.update(session, 1, data.dict())
|
||||||
|
|
||||||
return SnackResponse.success("Settings Updated")
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/webhooks/test")
|
@router.post("/webhooks/test")
|
||||||
def test_webhooks(
|
def test_webhooks(
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends, status, HTTPException
|
||||||
from mealie.db.database import db
|
from mealie.db.database import db
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
from mealie.schema.snackbar import SnackResponse
|
|
||||||
from mealie.schema.theme import SiteTheme
|
from mealie.schema.theme import SiteTheme
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
@ -16,12 +15,11 @@ def get_all_themes(session: Session = Depends(generate_session)):
|
|||||||
return db.themes.get_all(session)
|
return db.themes.get_all(session)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/themes/create")
|
@router.post("/themes/create", status_code=status.HTTP_201_CREATED)
|
||||||
def create_theme(data: SiteTheme, session: Session = Depends(generate_session), current_user=Depends(get_current_user)):
|
def create_theme(data: SiteTheme, session: Session = Depends(generate_session), current_user=Depends(get_current_user)):
|
||||||
""" Creates a site color theme database entry """
|
""" Creates a site color theme database entry """
|
||||||
db.themes.create(session, data.dict())
|
db.themes.create(session, data.dict())
|
||||||
|
|
||||||
return SnackResponse.success("Theme Saved")
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/themes/{theme_name}")
|
@router.get("/themes/{theme_name}")
|
||||||
@ -30,7 +28,7 @@ def get_single_theme(theme_name: str, session: Session = Depends(generate_sessio
|
|||||||
return db.themes.get(session, theme_name)
|
return db.themes.get(session, theme_name)
|
||||||
|
|
||||||
|
|
||||||
@router.put("/themes/{theme_name}")
|
@router.put("/themes/{theme_name}", status_code=status.HTTP_200_OK)
|
||||||
def update_theme(
|
def update_theme(
|
||||||
theme_name: str,
|
theme_name: str,
|
||||||
data: SiteTheme,
|
data: SiteTheme,
|
||||||
@ -40,12 +38,11 @@ def update_theme(
|
|||||||
""" Update a theme database entry """
|
""" Update a theme database entry """
|
||||||
db.themes.update(session, theme_name, data.dict())
|
db.themes.update(session, theme_name, data.dict())
|
||||||
|
|
||||||
return SnackResponse.info(f"Theme Updated: {theme_name}")
|
|
||||||
|
|
||||||
|
@router.delete("/themes/{theme_name}", status_code=status.HTTP_200_OK)
|
||||||
@router.delete("/themes/{theme_name}")
|
|
||||||
def delete_theme(theme_name: str, session: Session = Depends(generate_session), current_user=Depends(get_current_user)):
|
def delete_theme(theme_name: str, session: Session = Depends(generate_session), current_user=Depends(get_current_user)):
|
||||||
""" Deletes theme from the database """
|
""" Deletes theme from the database """
|
||||||
|
try:
|
||||||
db.themes.delete(session, theme_name)
|
db.themes.delete(session, theme_name)
|
||||||
|
except:
|
||||||
return SnackResponse.error(f"Theme Deleted: {theme_name}")
|
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||||
|
@ -7,7 +7,6 @@ from mealie.core import security
|
|||||||
from mealie.core.security import authenticate_user
|
from mealie.core.security import authenticate_user
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
from mealie.schema.snackbar import SnackResponse
|
|
||||||
from mealie.schema.user import UserInDB
|
from mealie.schema.user import UserInDB
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
@ -28,15 +27,11 @@ def get_token(
|
|||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
detail="Incorrect username or password",
|
|
||||||
headers={"WWW-Authenticate": "Bearer"},
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
)
|
)
|
||||||
|
|
||||||
access_token = security.create_access_token(dict(sub=email))
|
access_token = security.create_access_token(dict(sub=email))
|
||||||
return SnackResponse.success(
|
return {"access_token": access_token, "token_type": "bearer"}
|
||||||
"User Successfully Logged In",
|
|
||||||
{"access_token": access_token, "token_type": "bearer"},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/refresh")
|
@router.get("/refresh")
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import shutil
|
import shutil
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, File, UploadFile
|
from fastapi import APIRouter, Depends, File, UploadFile, status, HTTPException
|
||||||
from fastapi.responses import FileResponse
|
from fastapi.responses import FileResponse
|
||||||
from mealie.core import security
|
from mealie.core import security
|
||||||
from mealie.core.config import app_dirs, settings
|
from mealie.core.config import app_dirs, settings
|
||||||
@ -9,7 +9,6 @@ from mealie.core.security import get_password_hash, verify_password
|
|||||||
from mealie.db.database import db
|
from mealie.db.database import db
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
from mealie.schema.snackbar import SnackResponse
|
|
||||||
from mealie.schema.user import ChangePassword, UserBase, UserIn, UserInDB, UserOut
|
from mealie.schema.user import ChangePassword, UserBase, UserIn, UserInDB, UserOut
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
@ -25,8 +24,7 @@ async def create_user(
|
|||||||
|
|
||||||
new_user.password = get_password_hash(new_user.password)
|
new_user.password = get_password_hash(new_user.password)
|
||||||
|
|
||||||
data = db.users.create(session, new_user.dict())
|
return db.users.create(session, new_user.dict())
|
||||||
return SnackResponse.success(f"User Created: {new_user.full_name}", data)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("", response_model=list[UserOut])
|
@router.get("", response_model=list[UserOut])
|
||||||
@ -35,10 +33,10 @@ async def get_all_users(
|
|||||||
session: Session = Depends(generate_session),
|
session: Session = Depends(generate_session),
|
||||||
):
|
):
|
||||||
|
|
||||||
if current_user.admin:
|
if not current_user.admin:
|
||||||
|
raise HTTPException( status.HTTP_403_FORBIDDEN )
|
||||||
|
|
||||||
return db.users.get_all(session)
|
return db.users.get_all(session)
|
||||||
else:
|
|
||||||
return {"details": "user not authorized"}
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/self", response_model=UserOut)
|
@router.get("/self", response_model=UserOut)
|
||||||
@ -68,7 +66,6 @@ async def reset_user_password(
|
|||||||
new_password = get_password_hash(settings.DEFAULT_PASSWORD)
|
new_password = get_password_hash(settings.DEFAULT_PASSWORD)
|
||||||
db.users.update_password(session, id, new_password)
|
db.users.update_password(session, id, new_password)
|
||||||
|
|
||||||
return SnackResponse.success("Users Password Reset")
|
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{id}")
|
@router.put("/{id}")
|
||||||
@ -85,8 +82,7 @@ async def update_user(
|
|||||||
if current_user.id == id:
|
if current_user.id == id:
|
||||||
access_token = security.create_access_token(data=dict(sub=new_data.email))
|
access_token = security.create_access_token(data=dict(sub=new_data.email))
|
||||||
token = {"access_token": access_token, "token_type": "bearer"}
|
token = {"access_token": access_token, "token_type": "bearer"}
|
||||||
|
return token
|
||||||
return SnackResponse.success("User Updated", token)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{id}/image")
|
@router.get("/{id}/image")
|
||||||
@ -121,10 +117,8 @@ async def update_user_image(
|
|||||||
with dest.open("wb") as buffer:
|
with dest.open("wb") as buffer:
|
||||||
shutil.copyfileobj(profile_image.file, buffer)
|
shutil.copyfileobj(profile_image.file, buffer)
|
||||||
|
|
||||||
if dest.is_file:
|
if not dest.is_file:
|
||||||
return SnackResponse.success("File uploaded")
|
raise HTTPException( status.HTTP_500_INTERNAL_SERVER_ERROR )
|
||||||
else:
|
|
||||||
return SnackResponse.error("Failure uploading file")
|
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{id}/password")
|
@router.put("/{id}/password")
|
||||||
@ -139,12 +133,12 @@ async def update_password(
|
|||||||
match_passwords = verify_password(password_change.current_password, current_user.password)
|
match_passwords = verify_password(password_change.current_password, current_user.password)
|
||||||
match_id = current_user.id == id
|
match_id = current_user.id == id
|
||||||
|
|
||||||
if match_passwords and match_id:
|
if not ( match_passwords and match_id ):
|
||||||
|
raise HTTPException( status.HTTP_401_UNAUTHORIZED )
|
||||||
|
|
||||||
new_password = get_password_hash(password_change.new_password)
|
new_password = get_password_hash(password_change.new_password)
|
||||||
db.users.update_password(session, id, new_password)
|
db.users.update_password(session, id, new_password)
|
||||||
return SnackResponse.success("Password Updated")
|
|
||||||
else:
|
|
||||||
return SnackResponse.error("Existing password does not match")
|
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{id}")
|
@router.delete("/{id}")
|
||||||
@ -156,8 +150,13 @@ async def delete_user(
|
|||||||
""" Removes a user from the database. Must be the current user or a super user"""
|
""" Removes a user from the database. Must be the current user or a super user"""
|
||||||
|
|
||||||
if id == 1:
|
if id == 1:
|
||||||
return SnackResponse.error("Error! Cannot Delete Super User")
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail='SUPER_USER'
|
||||||
|
)
|
||||||
|
|
||||||
if current_user.id == id or current_user.admin:
|
if current_user.id == id or current_user.admin:
|
||||||
|
try:
|
||||||
db.users.delete(session, id)
|
db.users.delete(session, id)
|
||||||
return SnackResponse.error("User Deleted")
|
except:
|
||||||
|
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
@ -6,7 +6,6 @@ from mealie.db.db_setup import generate_session
|
|||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
from mealie.schema.sign_up import SignUpIn, SignUpOut, SignUpToken
|
from mealie.schema.sign_up import SignUpIn, SignUpOut, SignUpToken
|
||||||
from mealie.schema.snackbar import SnackResponse
|
|
||||||
from mealie.schema.user import UserIn, UserInDB
|
from mealie.schema.user import UserIn, UserInDB
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
@ -33,18 +32,16 @@ async def create_user_sign_up_key(
|
|||||||
):
|
):
|
||||||
""" Generates a Random Token that a new user can sign up with """
|
""" Generates a Random Token that a new user can sign up with """
|
||||||
|
|
||||||
if current_user.admin:
|
if not current_user.admin:
|
||||||
|
raise HTTPException( status.HTTP_403_FORBIDDEN )
|
||||||
|
|
||||||
sign_up = {
|
sign_up = {
|
||||||
"token": str(uuid.uuid1().hex),
|
"token": str(uuid.uuid1().hex),
|
||||||
"name": key_data.name,
|
"name": key_data.name,
|
||||||
"admin": key_data.admin,
|
"admin": key_data.admin,
|
||||||
}
|
}
|
||||||
db_entry = db.sign_ups.create(session, sign_up)
|
return db.sign_ups.create(session, sign_up)
|
||||||
|
|
||||||
return db_entry
|
|
||||||
|
|
||||||
else:
|
|
||||||
return {"details": "not authorized"}
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/{token}")
|
@router.post("/{token}")
|
||||||
@ -58,7 +55,7 @@ async def create_user_with_token(
|
|||||||
# Validate Token
|
# Validate Token
|
||||||
db_entry: SignUpOut = db.sign_ups.get(session, token, limit=1)
|
db_entry: SignUpOut = db.sign_ups.get(session, token, limit=1)
|
||||||
if not db_entry:
|
if not db_entry:
|
||||||
return SnackResponse.error("Invalid Token")
|
raise HTTPException( status.HTTP_401_UNAUTHORIZED )
|
||||||
|
|
||||||
# Create User
|
# Create User
|
||||||
new_user.admin = db_entry.admin
|
new_user.admin = db_entry.admin
|
||||||
@ -68,9 +65,6 @@ async def create_user_with_token(
|
|||||||
# DeleteToken
|
# DeleteToken
|
||||||
db.sign_ups.delete(session, token)
|
db.sign_ups.delete(session, token)
|
||||||
|
|
||||||
# Respond
|
|
||||||
return SnackResponse.success(f"User Created: {new_user.full_name}", data)
|
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{token}")
|
@router.delete("/{token}")
|
||||||
async def delete_token(
|
async def delete_token(
|
||||||
@ -79,8 +73,7 @@ async def delete_token(
|
|||||||
session: Session = Depends(generate_session),
|
session: Session = Depends(generate_session),
|
||||||
):
|
):
|
||||||
""" Removed a token from the database """
|
""" Removed a token from the database """
|
||||||
if current_user.admin:
|
if not current_user.admin:
|
||||||
|
raise HTTPException( status.HTTP_403_FORBIDDEN )
|
||||||
|
|
||||||
db.sign_ups.delete(session, token)
|
db.sign_ups.delete(session, token)
|
||||||
return SnackResponse.error("Sign Up Token Deleted")
|
|
||||||
else:
|
|
||||||
return {"details", "not authorized"}
|
|
||||||
|
@ -3,7 +3,6 @@ from typing import Optional
|
|||||||
|
|
||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends
|
||||||
from mealie.routes.deps import validate_file_token
|
from mealie.routes.deps import validate_file_token
|
||||||
from mealie.schema.snackbar import SnackResponse
|
|
||||||
from starlette.responses import FileResponse
|
from starlette.responses import FileResponse
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/utils", tags=["Utils"], include_in_schema=True)
|
router = APIRouter(prefix="/api/utils", tags=["Utils"], include_in_schema=True)
|
||||||
@ -14,7 +13,7 @@ async def download_file(file_path: Optional[Path] = Depends(validate_file_token)
|
|||||||
""" Uses a file token obtained by an active user to retrieve a file from the operating
|
""" Uses a file token obtained by an active user to retrieve a file from the operating
|
||||||
system. """
|
system. """
|
||||||
print("File Name:", file_path)
|
print("File Name:", file_path)
|
||||||
if file_path.is_file():
|
if not file_path.is_file():
|
||||||
|
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||||
|
|
||||||
return FileResponse(file_path, media_type="application/octet-stream", filename=file_path.name)
|
return FileResponse(file_path, media_type="application/octet-stream", filename=file_path.name)
|
||||||
else:
|
|
||||||
return SnackResponse.error("No File Found")
|
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
class SnackResponse:
|
|
||||||
@staticmethod
|
|
||||||
def _create_response(message: str, type: str, additional_data: dict = None) -> dict:
|
|
||||||
|
|
||||||
snackbar = {"snackbar": {"text": message, "type": type}}
|
|
||||||
|
|
||||||
if additional_data:
|
|
||||||
snackbar.update(additional_data)
|
|
||||||
|
|
||||||
return snackbar
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def success(message: str, additional_data: dict = None) -> dict:
|
|
||||||
return SnackResponse._create_response(message, "success", additional_data)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def info(message: str, additional_data: dict = None) -> dict:
|
|
||||||
return SnackResponse._create_response(message, "info", additional_data)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def warning(message: str, additional_data: dict = None) -> dict:
|
|
||||||
return SnackResponse._create_response(message, "warning", additional_data)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def error(message: str, additional_data: dict = None) -> dict:
|
|
||||||
return SnackResponse._create_response(message, "error", additional_data)
|
|
@ -13,12 +13,7 @@ def group_data():
|
|||||||
def test_create_group(api_client: TestClient, api_routes: AppRoutes, token):
|
def test_create_group(api_client: TestClient, api_routes: AppRoutes, token):
|
||||||
response = api_client.post(api_routes.groups, json={"name": "Test Group"}, headers=token)
|
response = api_client.post(api_routes.groups, json={"name": "Test Group"}, headers=token)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 201
|
||||||
|
|
||||||
assert json.loads(response.content) == {
|
|
||||||
"snackbar": {"text": "User Group Created", "type": "success"},
|
|
||||||
"created": True,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_self_group(api_client: TestClient, api_routes: AppRoutes, token):
|
def test_get_self_group(api_client: TestClient, api_routes: AppRoutes, token):
|
||||||
@ -42,7 +37,6 @@ def test_update_group(api_client: TestClient, api_routes: AppRoutes, token):
|
|||||||
# Test Update
|
# Test Update
|
||||||
response = api_client.put(api_routes.groups_id(2), json=new_data, headers=token)
|
response = api_client.put(api_routes.groups_id(2), json=new_data, headers=token)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert json.loads(response.text) == {"snackbar": {"text": "Group Settings Updated", "type": "success"}}
|
|
||||||
|
|
||||||
# Validate Changes
|
# Validate Changes
|
||||||
response = api_client.get(api_routes.groups, headers=token)
|
response = api_client.get(api_routes.groups, headers=token)
|
||||||
@ -51,13 +45,13 @@ def test_update_group(api_client: TestClient, api_routes: AppRoutes, token):
|
|||||||
assert next(id_2) == new_data
|
assert next(id_2) == new_data
|
||||||
|
|
||||||
|
|
||||||
def test_block_delete(api_client: TestClient, api_routes: AppRoutes, token):
|
def test_home_group_not_deletable(api_client: TestClient, api_routes: AppRoutes, token):
|
||||||
response = api_client.delete(api_routes.groups_id(1), headers=token)
|
response = api_client.delete(api_routes.groups_id(1), headers=token)
|
||||||
|
|
||||||
assert json.loads(response.text) == {"snackbar": {"text": "Cannot delete default group", "type": "error"}}
|
assert response.status_code == 400
|
||||||
|
|
||||||
|
|
||||||
def test_delete_group(api_client: TestClient, api_routes: AppRoutes, token):
|
def test_delete_group(api_client: TestClient, api_routes: AppRoutes, token):
|
||||||
response = api_client.delete(api_routes.groups_id(2), headers=token)
|
response = api_client.delete(api_routes.groups_id(2), headers=token)
|
||||||
|
|
||||||
assert json.loads(response.text) is None
|
assert response.status_code == 200
|
||||||
|
@ -50,7 +50,7 @@ def test_create_mealplan(api_client: TestClient, api_routes: AppRoutes, slug_1,
|
|||||||
meal_plan = get_meal_plan_template(slug_1, slug_2)
|
meal_plan = get_meal_plan_template(slug_1, slug_2)
|
||||||
|
|
||||||
response = api_client.post(api_routes.meal_plans_create, json=meal_plan, headers=token)
|
response = api_client.post(api_routes.meal_plans_create, json=meal_plan, headers=token)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 201
|
||||||
|
|
||||||
|
|
||||||
def test_read_mealplan(api_client: TestClient, api_routes: AppRoutes, slug_1, slug_2, token):
|
def test_read_mealplan(api_client: TestClient, api_routes: AppRoutes, slug_1, slug_2, token):
|
||||||
|
@ -62,7 +62,7 @@ def test_default_theme(api_client: TestClient, api_routes: AppRoutes, default_th
|
|||||||
def test_create_theme(api_client: TestClient, api_routes: AppRoutes, new_theme, token):
|
def test_create_theme(api_client: TestClient, api_routes: AppRoutes, new_theme, token):
|
||||||
|
|
||||||
response = api_client.post(api_routes.themes_create, json=new_theme, headers=token)
|
response = api_client.post(api_routes.themes_create, json=new_theme, headers=token)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 201
|
||||||
|
|
||||||
response = api_client.get(api_routes.themes_theme_name(new_theme.get("name")), headers=token)
|
response = api_client.get(api_routes.themes_theme_name(new_theme.get("name")), headers=token)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
Loading…
x
Reference in New Issue
Block a user