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