Compare commits
No commits in common. "32fb68c5ea65e1734a58e2fd7a2df94cfbf81a29" and "334bb97d358667676aca1e92cae273f4cb4f10f3" have entirely different histories.
32fb68c5ea
...
334bb97d35
@ -6,28 +6,27 @@ import ListSecrets from "./components/ListSecrets.vue";
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div id="header" class="header">
|
||||
<div id="header">
|
||||
<span class="logo">FastAuth</span>
|
||||
<div class="header-buttons" v-if="loggedin">
|
||||
<button class="header-button" title="Refresh secrets" @click="refresh">
|
||||
<img src="./assets/refresh-icon.png" class="button-icon" />
|
||||
</button>
|
||||
<button class="header-button" title="logout" @click="logout">
|
||||
<img src="./assets/logout-icon.png" class="button-icon" />
|
||||
</button>
|
||||
<el-button type="primary" class="ml-2" @click="creationDialog = true">
|
||||
Create
|
||||
</el-button>
|
||||
<el-button type="warning" class="ml-2" @click="logout">Logout</el-button>
|
||||
<el-button type="error" @click="refresh">Refresh</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timer" :style="{ width: timerWidth + '%' }"></div>
|
||||
|
||||
<el-dialog v-model="creationDialog" title="Add a new TOTP secret" width="80vw">
|
||||
<CreateSecret @close="secretSaved" />
|
||||
<CreateSecret @close="creationDialog = false" />
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="editDialog" title="Edit TOTP secret" width="80vw">
|
||||
<CreateSecret :editSecret="editingSecret" @close="secretSaved" />
|
||||
<CreateSecret :editSecret="editingSecret" @close="editDialog = false" />
|
||||
</el-dialog>
|
||||
|
||||
<div class="container">
|
||||
<div class="timer" v-if="loggedin" :style="{ width: timerWidth + '%' }"></div>
|
||||
<HomePage
|
||||
msg="You did it!"
|
||||
@loggedin="
|
||||
@ -42,7 +41,6 @@ import ListSecrets from "./components/ListSecrets.vue";
|
||||
</el-button> -->
|
||||
<ListSecrets :key="listUpdated" v-if="showSecrets && loggedin" @edit="editSecret" />
|
||||
</div>
|
||||
<button class="create-floating" @click="creationDialog = true">+</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -57,7 +55,7 @@ export default {
|
||||
apiBaseUrl: "http://localhost:8000",
|
||||
editDialog: false,
|
||||
editingSecret: {},
|
||||
timerWidth: 0,
|
||||
timerWidth: 100,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@ -68,8 +66,9 @@ export default {
|
||||
|
||||
secretSaved() {
|
||||
this.creationDialog = false;
|
||||
this.editDialog = false;
|
||||
console.log("before update", this.listUpdated);
|
||||
this.listUpdated += 1;
|
||||
console.log("after update", this.listUpdated);
|
||||
},
|
||||
|
||||
async validateToken() {
|
||||
@ -113,12 +112,15 @@ export default {
|
||||
},
|
||||
|
||||
startTimer() {
|
||||
// console.log("start timer called");
|
||||
this.interval = setInterval(() => {
|
||||
const now = new Date();
|
||||
const seconds = now.getSeconds();
|
||||
const remainingTime = (seconds > 30 ? 60 : 30) - seconds;
|
||||
// console.log(remainingTime);
|
||||
this.timerWidth = (remainingTime / 30) * 100;
|
||||
if (remainingTime === 30) {
|
||||
this.refresh();
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
},
|
||||
@ -137,9 +139,19 @@ export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style>
|
||||
.header {
|
||||
position: absolute;
|
||||
width: 100vw;
|
||||
height: 3rem;
|
||||
padding: 0.3rem;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background-color: aquamarine;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin-top: 6vh;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.logoutBtn {
|
||||
@ -151,10 +163,10 @@ export default {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.header {
|
||||
#header {
|
||||
width: 100vw;
|
||||
height: 48px;
|
||||
position: fixed;
|
||||
height: 3rem;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background-color: rgb(170, 247, 247);
|
||||
@ -163,45 +175,15 @@ export default {
|
||||
padding: 0 12px 0 12px;
|
||||
justify-content: space-between;
|
||||
box-shadow: 2px 0px 8px #aaa;
|
||||
z-index: 999;
|
||||
}
|
||||
.logo {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 700;
|
||||
margin-left: 2vw;
|
||||
}
|
||||
|
||||
.timer {
|
||||
position: fixed;
|
||||
bottom: 4px;
|
||||
margin-top: 1.2rem;
|
||||
height: 0.3rem;
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
.create-floating {
|
||||
position: fixed;
|
||||
bottom: 4vh;
|
||||
right: 4vw;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border: none;
|
||||
border-radius: 20px;
|
||||
box-shadow: 4px 4px 6px #999;
|
||||
background-color: teal;
|
||||
color: white;
|
||||
font-size: 1.6rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button-icon {
|
||||
width: 24px;
|
||||
margin-top: 8px;
|
||||
height: 24px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.header-button {
|
||||
border: none;
|
||||
background: none;
|
||||
}
|
||||
</style>
|
||||
|
@ -65,7 +65,7 @@ body {
|
||||
transition:
|
||||
color 0.5s,
|
||||
background-color 0.5s; */
|
||||
line-height: 1.2;
|
||||
line-height: 1.6;
|
||||
font-family:
|
||||
/* Inter,
|
||||
-apple-system,
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 13 KiB |
@ -1,11 +1,10 @@
|
||||
@import './base.css';
|
||||
|
||||
#app {
|
||||
/* max-width: 1280px; */
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 1rem;
|
||||
padding: 2rem;
|
||||
font-weight: normal;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
a,
|
||||
@ -25,13 +24,12 @@ a,
|
||||
@media (min-width: 1024px) {
|
||||
body {
|
||||
display: flex;
|
||||
/* place-items: center; */
|
||||
margin-top: 2rem;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
#app {
|
||||
/* display: grid; */
|
||||
/* grid-template-columns: 1fr 1fr; */
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 18 KiB |
@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<br />
|
||||
<div class="filter-buttons">
|
||||
<el-button-group>
|
||||
<el-button
|
||||
@ -15,30 +16,6 @@
|
||||
>Clear</el-button
|
||||
>
|
||||
</el-button-group>
|
||||
<div>
|
||||
<el-form-item label="Show OTPs">
|
||||
<el-switch v-model="showSecrets" active-value="yes" inactive-value="no" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="sort-options">
|
||||
<span>
|
||||
<el-form-item label="Sort">
|
||||
<el-select
|
||||
v-model="currentSort"
|
||||
placeholder="Select"
|
||||
style="width: 80px"
|
||||
@change="sortItems"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in sortOptions"
|
||||
:key="item.key"
|
||||
:label="item.name"
|
||||
:value="item.key"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -49,8 +26,6 @@
|
||||
:username="secret.username"
|
||||
:secret="secret.secret"
|
||||
:id="secret.id"
|
||||
:showOtp="showSecrets"
|
||||
:key="showSecrets"
|
||||
@edit="editSecret"
|
||||
/>
|
||||
</span>
|
||||
@ -82,18 +57,6 @@ export default {
|
||||
currentFilter: [],
|
||||
loadCards: false,
|
||||
showClear: false,
|
||||
showSecrets: "yes",
|
||||
currentSort: "asc",
|
||||
sortOptions: [
|
||||
{
|
||||
name: "A-Z",
|
||||
key: "asc",
|
||||
},
|
||||
{
|
||||
name: "Z-A",
|
||||
key: "desc",
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
|
||||
@ -124,7 +87,6 @@ export default {
|
||||
this.secretsList.push(row);
|
||||
});
|
||||
this.filteredSecretsList = this.secretsList;
|
||||
this.sortItems();
|
||||
this.loadCards = true;
|
||||
},
|
||||
|
||||
@ -167,14 +129,6 @@ export default {
|
||||
this.filteredSecretsList = this.secretsList;
|
||||
this.showClear = false;
|
||||
},
|
||||
|
||||
sortItems() {
|
||||
if (this.currentSort === "asc") {
|
||||
this.filteredSecretsList.sort((a, b) => a.issuer.localeCompare(b.issuer));
|
||||
} else if (this.currentSort === "desc") {
|
||||
this.filteredSecretsList.sort((a, b) => b.issuer.localeCompare(a.issuer));
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
computed: {},
|
||||
@ -183,28 +137,11 @@ export default {
|
||||
currentFilter: function () {
|
||||
const filteredList = this.secretsList.filter(this.checkFirstLetter);
|
||||
this.filteredSecretsList = filteredList;
|
||||
this.sortItems();
|
||||
},
|
||||
|
||||
currentSort: function () {
|
||||
localStorage.setItem("currentSort", this.currentSort);
|
||||
},
|
||||
|
||||
showSecrets: function () {
|
||||
localStorage.setItem("showSecrets", this.showSecrets);
|
||||
},
|
||||
|
||||
secretsList: function () {
|
||||
this.currentSort = localStorage.getItem("currentSort");
|
||||
this.filteredSecretsList = this.secretsList;
|
||||
this.sortItems();
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.listSecrets();
|
||||
this.showSecrets = localStorage.getItem("showSecrets");
|
||||
this.currentSort = localStorage.getItem("currentSort");
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@ -214,21 +151,13 @@ export default {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-evenly;
|
||||
max-width: 96vw;
|
||||
max-width: 90vw;
|
||||
}
|
||||
|
||||
.filter-buttons {
|
||||
width: 96vw;
|
||||
width: 90%;
|
||||
display: flex;
|
||||
margin: 40px 0 20px 0;
|
||||
justify-content: space-around;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.sort-options {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
min-width: 80px;
|
||||
margin-bottom: 20px;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,28 +1,25 @@
|
||||
<template>
|
||||
<div
|
||||
class="wrapper"
|
||||
@mouseenter="[(otpShown = true), (iconOpacity = 1)]"
|
||||
@mouseleave="
|
||||
[(otpShown = showOtp === 'yes' ? true : tapped ? true : false), (iconOpacity = 0.1)]
|
||||
"
|
||||
>
|
||||
<div class="wrapper">
|
||||
<div class="card-top">
|
||||
<span class="issuer">{{ issuer }}</span>
|
||||
<button class="card-button" @click="editSecret">
|
||||
<button class="edit-button" @click="editSecret">
|
||||
<img src="../assets/edit-icon.png" class="button-icon" /></button
|
||||
><br />
|
||||
</div>
|
||||
|
||||
<span class="user">{{ username }}</span> <br />
|
||||
<div class="otp-container">
|
||||
<span class="otp" @click="[tapReveal($event), copyOtp($event)]">
|
||||
<span v-if="otpShown">{{ this.otp }}</span>
|
||||
<span v-else> •••••• </span>
|
||||
</span>
|
||||
<button class="card-button" @click="copyOtp">
|
||||
<img src="../assets/copy-icon.png" class="button-icon" />
|
||||
</button>
|
||||
</div>
|
||||
<span
|
||||
class="otp"
|
||||
@click="copyOtp"
|
||||
@mouseenter="otpHidden = false"
|
||||
@mouseleave="otpHidden = true"
|
||||
>
|
||||
<span v-if="otpHidden">******</span>
|
||||
<span v-else>{{ generateTotp(secret) }}</span>
|
||||
</span>
|
||||
<button class="edit-button" @click="copyOtp">
|
||||
<img src="../assets/copy-icon.png" class="button-icon" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -34,29 +31,24 @@ export default {
|
||||
issuer: String,
|
||||
username: String,
|
||||
secret: String,
|
||||
showOtp: {
|
||||
type: String,
|
||||
default: "no",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
otp: 123456,
|
||||
notes: "",
|
||||
otpShown: false,
|
||||
iconOpacity: 0.1,
|
||||
tapped: false,
|
||||
revealTime: 10,
|
||||
otpHidden: true,
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
generateTotp() {
|
||||
const { otp, expires } = TOTP.generate(this.secret);
|
||||
const now = new Date();
|
||||
const remainingTime = expires - now;
|
||||
generateTotp(secret) {
|
||||
const { otp, expires } = TOTP.generate(secret);
|
||||
const remaining = (expires - Date.now()) / 30000;
|
||||
this.remainingTime = remaining / 1000;
|
||||
this.timerWidth = remaining * 100;
|
||||
// console.log("hello");
|
||||
this.otp = otp;
|
||||
setTimeout(this.generateTotp, remainingTime);
|
||||
return otp;
|
||||
},
|
||||
|
||||
async copyOtp() {
|
||||
@ -70,33 +62,18 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
async tapReveal() {
|
||||
console.log("tapped");
|
||||
this.otpShown = true;
|
||||
this.tapped = true;
|
||||
setTimeout(() => {
|
||||
this.otpShown = this.showOtp === "yes" ? true : false;
|
||||
this.tapped = false;
|
||||
}, this.revealTime * 1000);
|
||||
},
|
||||
|
||||
editSecret() {
|
||||
this.$emit("edit", this.id);
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.otpShown = this.showOtp === "yes" ? true : false;
|
||||
this.generateTotp();
|
||||
},
|
||||
|
||||
created() {},
|
||||
mounted() {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.wrapper {
|
||||
height: 90px;
|
||||
height: 110px;
|
||||
width: 160px;
|
||||
/* border: 1px solid black; */
|
||||
border-radius: 4px;
|
||||
@ -115,30 +92,22 @@ export default {
|
||||
}
|
||||
|
||||
.otp {
|
||||
margin-top: 0.4rem;
|
||||
font-size: 1.8rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.card-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.card-button {
|
||||
.edit-button {
|
||||
position: relative;
|
||||
right: 0;
|
||||
margin-left: auto;
|
||||
border: none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.button-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
opacity: v-bind("iconOpacity");
|
||||
}
|
||||
|
||||
.otp-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
||||
|
6
main.py
6
main.py
@ -131,11 +131,7 @@ async def create_secret(secret: Secret, current_user: dict = Depends(get_current
|
||||
text = f.read()
|
||||
if text:
|
||||
data.extend(json.loads(text))
|
||||
|
||||
if data:
|
||||
secret_id = max(i['id'] for i in data) + 1
|
||||
else:
|
||||
secret_id = 0
|
||||
secret_id = max(i['id'] for i in data) + 1
|
||||
secret.id = secret_id
|
||||
secret.user_id = current_user['id']
|
||||
encryption_key = current_user['encryption_key'].encode()
|
||||
|
@ -36,6 +36,6 @@ typer==0.12.3
|
||||
typing_extensions==4.12.2
|
||||
ujson==5.10.0
|
||||
uvicorn==0.30.1
|
||||
# uvloop==0.19.0
|
||||
uvloop==0.19.0
|
||||
watchfiles==0.22.0
|
||||
websockets==12.0
|
||||
|
Loading…
Reference in New Issue
Block a user