profile image upload

This commit is contained in:
hay-kot 2021-02-25 19:10:14 -09:00
commit 1f16d0cb39
9 changed files with 127 additions and 24 deletions

View file

@ -12,7 +12,6 @@ const categoryURLs = {
export default {
async get_all() {
let response = await apiReq.get(categoryURLs.get_all);
console.log("All Cats", response.data);
return response.data;
},
async get_recipes_in_category(category) {

View file

@ -117,7 +117,6 @@ export default {
},
async mounted() {
let settings = await api.settings.requestAll();
console.log("Settings", settings.planCategories);
this.items = await api.recipes.getAllByCategory(settings.planCategories);
},

View file

@ -15,6 +15,7 @@ export default {
url: String,
text: { default: "Upload" },
icon: { default: "mdi-cloud-upload" },
fileName: { defaul: "archive" },
},
data: () => ({
file: null,
@ -32,7 +33,7 @@ export default {
if (this.file != null) {
this.isSelecting = true;
let formData = new FormData();
formData.append("archive", this.file);
formData.append(this.fileName, this.file);
await api.utils.uploadFile(this.url, formData);

View file

@ -0,0 +1,17 @@
export const initials = {
computed: {
initials() {
const allNames = this.user.fullName.trim().split(" ");
const initials = allNames.reduce(
(acc, curr, index) => {
if (index === 0 || index === allNames.length - 1) {
acc = `${acc}${curr.charAt(0).toUpperCase()}`;
}
return acc;
},
[""]
);
return initials;
},
},
};

View file

@ -1,6 +1,5 @@
import { store } from "@/store";
export const user = {
data() {},
computed: {
user() {
return store.getters.getUserData;
@ -8,5 +7,18 @@ export const user = {
loggedIn() {
return store.getters.getIsLoggedIn;
},
initials() {
const allNames = this.user.fullName.trim().split(" ");
const initials = allNames.reduce(
(acc, curr, index) => {
if (index === 0 || index === allNames.length - 1) {
acc = `${acc}${curr.charAt(0).toUpperCase()}`;
}
return acc;
},
[""]
);
return initials;
},
},
};

View file

@ -33,18 +33,22 @@
</v-app-bar>
<v-card-text>
<v-container>
<v-form ref="newUser">
<v-row>
<v-col cols="12" sm="12" md="6">
<v-text-field
v-model="editedItem.fullName"
label="Full Name"
:rules="[existsRule]"
validate-on-blur
></v-text-field>
</v-col>
<v-col cols="12" sm="12" md="6">
<v-text-field
v-model="editedItem.email"
label="Email"
:rules="[existsRule, emailRule]"
validate-on-blur
></v-text-field>
</v-col>
<v-col cols="12" sm="12" md="6">
@ -57,6 +61,7 @@
<v-text-field
v-model="editedItem.password"
label="User Password"
:rules="[existsRule, minRule]"
></v-text-field>
</v-col>
<v-col cols="12" sm="12" md="3">
@ -66,7 +71,7 @@
></v-switch>
</v-col>
</v-row>
</v-container>
</v-form>
</v-card-text>
<v-card-actions>
@ -121,8 +126,10 @@
<script>
import Confirmation from "@/components/UI/Confirmation";
import api from "@/api";
import { validators } from "@/mixins/validators";
export default {
components: { Confirmation },
mixins: [validators],
data: () => ({
dialog: false,
activeId: null,
@ -145,6 +152,7 @@ export default {
editedItem: {
id: 0,
fullName: "",
password: "",
email: "",
family: "",
admin: false,
@ -152,6 +160,7 @@ export default {
defaultItem: {
id: 0,
fullName: "",
password: "",
email: "",
family: "",
admin: false,
@ -228,11 +237,12 @@ export default {
async save() {
if (this.editedIndex > -1) {
api.users.update(this.editedItem);
} else {
this.close();
} else if (this.$refs.newUser.validate()) {
api.users.create(this.editedItem);
this.close();
}
await this.initialize();
this.close();
},
},
};

View file

@ -21,11 +21,20 @@
<v-card-text>
<v-row>
<v-col cols="12" md="3" align="center" justify="center">
<v-avatar color="accent" size="120" class="mr-2" v-if="!loading">
<v-avatar
color="accent"
size="120"
v-if="!loading"
class="white--text headline mr-2"
>
<img
src="https://cdn.vuetifyjs.com/images/john.jpg"
alt="John"
:src="userProfileImage"
v-if="!hideImage"
@error="hideImage = true"
/>
<div v-else>
{{ initials }}
</div>
</v-avatar>
</v-col>
<v-col cols="12" md="9">
@ -60,7 +69,12 @@
</v-card-text>
<v-card-actions>
<UploadBtn icon="mdi-image-area" text="Upload Photo" />
<UploadBtn
icon="mdi-image-area"
text="Upload Photo"
:url="userProfileImage"
file-name="profile_image"
/>
<v-spacer></v-spacer>
<v-btn color="success" class="mr-2" @click="updateUser">
@ -110,17 +124,16 @@
</v-form>
</v-card-text>
<v-card-actions>
<v-btn icon @click="showPassword = !showPassword" :loading="passwordLoading">
<v-btn
icon
@click="showPassword = !showPassword"
:loading="passwordLoading"
>
<v-icon v-if="!showPassword">mdi-eye-off</v-icon>
<v-icon v-else> mdi-eye </v-icon>
</v-btn>
<v-spacer></v-spacer>
<v-btn
color="accent"
class="mr-2"
@click="changePassword"
>
<v-btn color="accent" class="mr-2" @click="changePassword">
<v-icon left> mdi-lock </v-icon>
{{ $t("settings.change-password") }}
</v-btn>
@ -135,13 +148,15 @@
import UploadBtn from "@/components/UI/UploadBtn";
import api from "@/api";
import { validators } from "@/mixins/validators";
import { initials } from "@/mixins/initials";
export default {
components: {
UploadBtn,
},
mixins: [validators],
mixins: [validators, initials],
data() {
return {
hideImage: false,
passwordLoading: false,
password: {
current: "",
@ -160,6 +175,12 @@ export default {
};
},
computed: {
userProfileImage() {
return `api/users/${this.user.id}/image`;
},
},
async mounted() {
this.refreshProfile();
},

View file

@ -12,7 +12,7 @@ def ensure_dirs():
# Register ENV
ENV = CWD.joinpath(".env") #! I'm Broken Fix Me!
ENV = CWD.joinpath(".env") #! I'm Broken Fix Me!
dotenv.load_dotenv(ENV)
@ -45,6 +45,7 @@ MIGRATION_DIR = DATA_DIR.joinpath("migration")
NEXTCLOUD_DIR = MIGRATION_DIR.joinpath("nextcloud")
CHOWDOWN_DIR = MIGRATION_DIR.joinpath("chowdown")
TEMPLATE_DIR = DATA_DIR.joinpath("templates")
USER_DIR = DATA_DIR.joinpath("users")
SQLITE_DIR = DATA_DIR.joinpath("db")
RECIPE_DATA_DIR = DATA_DIR.joinpath("recipes")
TEMP_DIR = DATA_DIR.joinpath(".temp")
@ -59,7 +60,8 @@ REQUIRED_DIRS = [
SQLITE_DIR,
NEXTCLOUD_DIR,
CHOWDOWN_DIR,
RECIPE_DATA_DIR
RECIPE_DATA_DIR,
USER_DIR,
]
ensure_dirs()

View file

@ -1,10 +1,13 @@
import shutil
from datetime import timedelta
from core.config import USER_DIR
from core.security import get_password_hash, verify_password
from db.database import db
from db.db_setup import generate_session
from fastapi import APIRouter, Depends
from routes.deps import manager, query_user
from fastapi import APIRouter, Depends, File, UploadFile
from fastapi.responses import FileResponse
from routes.deps import manager
from schema.snackbar import SnackResponse
from schema.user import ChangePassword, UserBase, UserIn, UserInDB, UserOut
from sqlalchemy.orm.session import Session
@ -74,6 +77,45 @@ async def update_user(
return SnackResponse.success("User Updated", access_token)
@router.get("/{id}/image")
async def get_user_image(id: str):
""" Returns a users profile picture """
user_dir = USER_DIR.joinpath(id)
for recipe_image in user_dir.glob("profile_image.*"):
print(recipe_image)
return FileResponse(recipe_image)
else:
return False
@router.post("/{id}/image")
async def update_user_image(
id: str,
profile_image: UploadFile = File(...),
current_user: UserInDB = Depends(manager),
):
""" Updates a User Image """
extension = profile_image.filename.split(".")[-1]
USER_DIR.joinpath(id).mkdir(parents=True, exist_ok=True)
try:
[x.unlink() for x in USER_DIR.join(id).glob("profile_image.*")]
except:
pass
dest = USER_DIR.joinpath(id, f"profile_image.{extension}")
with dest.open("wb") as buffer:
shutil.copyfileobj(profile_image.file, buffer)
if dest.is_file:
return SnackResponse.success("Backup uploaded")
else:
return SnackResponse.error("Failure uploading file")
@router.put("/{id}/password")
async def update_password(
id: int,