EDS Base Implementation

This commit is contained in:
root
2022-08-18 18:26:09 +05:30
parent 29e549ab89
commit 7b9425aa4c
11 changed files with 966 additions and 94 deletions
+3
View File
@@ -25,10 +25,13 @@
"core-js": "^3.30.2",
"dexie": "^3.2.3",
"dompurify": "^2.4.5",
"ethers": "^5",
"futoin-hkdf": "^1.5.1",
"i18n-iso-countries": "^6.8.0",
"intl-messageformat": "^10.3.5",
"intl-unofficial-duration-unit-format": "^3.1.0",
"javascript-time-ago": "https://cf-ipfs.com/ipfs/QmPfsnf4YYW63ddBUVXkT9BNQyuGKMnzyFVSV2dcx3dpz1?filename=jta-2.5.9.tgz",
"libeds": "https://cf-ipfs.com/ipfs/QmeLULpS42DupKxR4wQxLGxqN8ybhV1ZJBVSYwZ7uNJMeH?filename=libeds-0.1.2.tar.gz",
"lodash-es": "^4.17.21",
"marked": "^3.0.8",
"mux.js": "^5.14.1",
+91 -4
View File
@@ -1,5 +1,5 @@
<template>
<v-app :style="bgStyles">
<v-app :style="bgStyles" v-if="initialized">
<v-app-bar
app
dense
@@ -123,6 +123,11 @@
<router-view />
</v-main>
</v-app>
<div class="loader-svg-appvue" v-else>
<svg class="circular">
<circle class="path" cx="50" cy="50" r="20" fill="none" stroke-width="5" stroke-miterlimit="10"></circle>
</svg>
</div>
</template>
<style lang="scss">
@@ -135,15 +140,84 @@
margin-right: 0;
}
}
.loader-svg-appvue {
$width: 100px;
$green: #008744;
$blue: #0057e7;
$red: #d62d20;
$yellow: #ffa700;
$white: #eee;
position: absolute;
width: $width;
height: $width;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
.circular {
animation: rotate 2s linear infinite;
height: $width;
position: relative;
width: $width;
}
.path {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
stroke: #b6463a;
animation: dash 1.5s ease-in-out infinite, color 6s ease-in-out infinite;
stroke-linecap: round;
}
@keyframes rotate {
100% {
transform: rotate(360deg);
}
}
@keyframes dash {
0% {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 89, 200;
stroke-dashoffset: -35;
}
100% {
stroke-dasharray: 89, 200;
stroke-dashoffset: -124;
}
}
@keyframes color {
100%,
0% {
stroke: $red;
}
40% {
stroke: $blue;
}
66% {
stroke: $green;
}
80%,
90% {
stroke: $yellow;
}
}
}
</style>
<script>
import { mdiBrightness6, mdiMagnify, mdiClose, mdiHome } from '@mdi/js'
import SearchMenu from '@/routes/SearchMenu'
import { changeLocale, SUPPORTED_LANGUAGES } from '@/plugins/i18n'
import AuthenticationModal from '@/components/AuthenticationModal'
import { COLOR_SCHEME_STATES } from '@/store/prefs-store'
import { syncPMDB } from '@/store/watched-videos-db'
import { initializeEDS } from '@/plugins/eds'
import { initializeLocalLocale, changeLocale, SUPPORTED_LANGUAGES } from '@/plugins/i18n'
export default {
name: 'App',
@@ -160,6 +234,7 @@ export default {
},
data: () => ({
initialized: false,
drawer: false,
// Only used on phones
searchMenuOpened: false,
@@ -266,8 +341,20 @@ export default {
},
created () {
this.$vuetify.rtl = this.$store.state.i18n.rtl
this.syncDarkMode()
initializeEDS()
.then(() => Promise.all([
syncPMDB(),
this.$store.dispatch('prefs/loadState'),
this.$store.dispatch('auth/initializeAuth'),
initializeLocalLocale(),
() => {
this.$vuetify.rtl = this.$store.state.i18n.rtl
},
this.syncDarkMode()
]))
.then(() => {
this.initialized = true
})
}
}
</script>
+30 -4
View File
@@ -24,8 +24,12 @@
<v-card>
<v-card-title>{{ $t('auth_dialog.title') }}</v-card-title>
<v-card-text>
<v-text-field v-model="username" :label="$t('auth_dialog.username')"/>
<v-text-field type="password" v-model="password" :label="$t('auth_dialog.password')"/>
<v-text-field v-model="username" :label="$t('auth_dialog.username')" />
<v-alert color="grey">
Suggested Username: <code>{{ suggestedUsername }}</code>
<v-btn icon @click="username = suggestedUsername"><v-icon>{{ mdiCheckAll }}</v-icon></v-btn>
</v-alert>
<v-text-field type="text" :value="password" label="Password (Autogenerated from the EDS private key per instance)" readonly />
<v-alert outlined color="error" v-if="error != null">{{ error }}</v-alert>
<v-btn @click="login" :disabled="requestInProgress" :loading="requestInProgress" color="primary" outlined large>
{{ $t('auth_dialog.log_in') }}
@@ -66,7 +70,7 @@
<v-btn @click="logOutAllDevices" outlined large>{{ $t('actions.logOutFromAllDevices') }}</v-btn>
<v-divider class="my-2" />
<h5 class="text-subtitle-1">{{ $t('actions.delAccountMessage') }}</h5>
<v-text-field type="password" v-model="password" label="Password"/>
<v-text-field type="text" :value="password" label="Password (Autogenerated from the EDS private key per instance)" readonly />
<v-btn outlined style="display: inline-block; white-space: normal; height: auto; padding: 8px" small @click="delAccount">{{ $t('actions.confirmPWToDelAcc') }}</v-btn>
</v-card-text>
</v-card>
@@ -74,7 +78,11 @@
</template>
<script>
import HKDF from 'futoin-hkdf'
import { mdiCheckAll } from '@mdi/js'
import { AuthenticationError } from '@/store/authentication-store'
import { EDS } from '@/plugins/eds'
export default {
name: 'AuthenticationModal',
@@ -84,9 +92,27 @@ export default {
requestInProgress: false,
dialogOpen: false,
username: '',
password: ''
suggestedUsername: '',
password: '',
mdiCheckAll
}),
watch: {
apiURL: 'calcStuff'
},
mounted () {
this.calcStuff()
},
computed: {
apiURL () {
return this.$store.getters['prefs/apiUrl']
}
},
methods: {
calcStuff () {
this.password = HKDF.expand('sha512', (512 / 8), EDS.privateKey, 48, this.apiURL).toString('base64')
this.suggestedUsername = HKDF.expand('sha512', (512 / 8), EDS.privateKey, 12, this.apiURL + '_username').toString('hex')
},
async doCall (path) {
this.error = null
try {
+8 -15
View File
@@ -4,7 +4,7 @@ import App from './App.vue'
import 'antar-hinted/antar.css'
import '@/styles/main.scss'
import { i18n, i18nInitialized } from '@/plugins/i18n'
import { i18n } from '@/plugins/i18n'
import vuetify from '@/plugins/vuetify'
import store from '@/store'
import router from '@/router'
@@ -12,17 +12,10 @@ import router from '@/router'
import './registerServiceWorker'
Vue.config.productionTip = false
Promise.all([
store.dispatch('prefs/loadState'),
store.dispatch('auth/initializeAuth'),
i18nInitialized
]).then(() => {
new Vue({
vuetify,
i18n,
store,
router,
render: h => h(App)
}).$mount('#app')
})
new Vue({
vuetify,
i18n,
store,
router,
render: h => h(App)
}).$mount('#app')
+12
View File
@@ -0,0 +1,12 @@
import { EncryptedDatabase } from 'libeds'
import { providers as EthersProviders } from 'ethers'
export const EDS = new EncryptedDatabase()
export function initializeEDS () {
return EDS.initialize(new EthersProviders.Web3Provider(window.ethereum, 'any'), {
appID: 'PipedMaterial',
url: process.env.VUE_APP_EDS_URL ?? 'wss://eds.gra.১.net:18001'
})
}
window.EDS = EDS
+4 -5
View File
@@ -3,6 +3,7 @@ import VueI18n from 'vue-i18n'
import TimeAgo from 'javascript-time-ago'
import * as LocaleMatcher from '@formatjs/intl-localematcher'
import { EDS } from '@/plugins/eds'
import store from '@/store'
import ENTranslations from '@/translations/en.json'
import ENTimeAgo from 'javascript-time-ago/locale/en.json'
@@ -187,15 +188,13 @@ export async function changeLocale (lang) {
loadFormatting(lang)
])
i18n.locale = lang
window.localStorage.setItem('LOCALE', lang)
await EDS.setKey('locale', lang)
}
function initializeLocalLocale () {
let lang = window.localStorage.getItem('LOCALE')
export async function initializeLocalLocale () {
let lang = await EDS.getKey('locale')
if (lang == null) {
lang = LocaleMatcher.match(navigator.languages, SUPPORTED_LANGUAGES, 'en')
}
return changeLocale(lang)
}
export const i18nInitialized = initializeLocalLocale()
+7 -15
View File
@@ -1,5 +1,6 @@
import axios from 'axios'
import { isPlainObject as _isPlainObject, set as _set } from 'lodash-es'
import { EDS } from '@/plugins/eds'
export class AuthenticationError extends Error {
constructor (message) {
@@ -28,14 +29,14 @@ const AuthenticationStore = {
isAuthenticated: true,
authToken: token
})
window.localStorage.setItem('AUTH', JSON.stringify(state.authStateByInstance))
EDS.setKey('AUTH', state.authStateByInstance).catch(e => console.error(e))
},
deleteAuthToken (state, { apiURL }) {
_set(state.authStateByInstance, [apiURL], {
isAuthenticated: false
})
window.localStorage.setItem('AUTH', JSON.stringify(state.authStateByInstance))
EDS.setKey('AUTH', state.authStateByInstance).catch(e => console.error(e))
}
},
@@ -52,10 +53,10 @@ const AuthenticationStore = {
},
actions: {
initializeAuth ({ commit }) {
const data = window.localStorage.getItem('AUTH')
async initializeAuth ({ commit }) {
const data = await EDS.getKey('AUTH')
if (data != null) {
commit('replaceAuth', JSON.parse(data))
commit('replaceAuth', data)
}
},
@@ -139,15 +140,6 @@ const AuthenticationStore = {
}
}
function initializeAuthEvents (store) {
window.addEventListener('storage', (storageEv) => {
if (storageEv.key === 'AUTH') {
store.commit('auth/replaceAuth', JSON.parse(storageEv.newValue))
}
})
}
export {
AuthenticationStore,
initializeAuthEvents
AuthenticationStore
}
+2 -3
View File
@@ -1,8 +1,8 @@
import Vue from 'vue'
import Vuex from 'vuex'
import { PrefsStore, initializePrefEvents } from '@/store/prefs-store'
import { AuthenticationStore, initializeAuthEvents } from '@/store/authentication-store'
import { initializePrefEvents, PrefsStore } from '@/store/prefs-store'
import { AuthenticationStore } from '@/store/authentication-store'
import { i18nStore } from '@/store/i18n-store'
Vue.use(Vuex)
@@ -16,6 +16,5 @@ const store = new Vuex.Store({
})
initializePrefEvents(store)
initializeAuthEvents(store)
export default store
+7 -9
View File
@@ -1,4 +1,5 @@
import { isString, set as _set } from 'lodash-es'
import { EDS } from '@/plugins/eds'
export const COLOR_SCHEME_STATES = {
LIGHT: 0,
@@ -43,7 +44,7 @@ const PrefsStore = {
value
}) {
_set(state.prefs, id, value)
window.localStorage.setItem('PREFERENCES', JSON.stringify(state.prefs))
EDS.setKey('preferences', state.prefs).catch(e => console.error(e))
},
replacePrefs (state, nextPrefs) {
@@ -51,17 +52,16 @@ const PrefsStore = {
}
},
actions: {
loadState ({ commit }) {
async loadState ({ commit }) {
try {
const cs = window.localStorage.getItem('COLOR_SCHEME')
if (isString(cs) && cs.length !== 0) {
commit('setColorScheme', { colorScheme: JSON.parse(cs) })
}
const jv = window.localStorage.getItem('PREFERENCES')
if (isString(jv) && jv.length !== 0) {
const p = JSON.parse(jv)
commit('replacePrefs', p)
const jv = await EDS.getKey('preferences')
if (jv != null) {
commit('replacePrefs', jv)
}
} catch (e) {
console.log('Error:', e)
@@ -113,9 +113,7 @@ const PrefsStore = {
function initializePrefEvents (store) {
window.addEventListener('storage', (storageEv) => {
if (storageEv.key === 'PREFERENCES') {
store.commit('prefs/replacePrefs', JSON.parse(storageEv.newValue))
} else if (storageEv.key === 'COLOR_SCHEME') {
if (storageEv.key === 'COLOR_SCHEME') {
store.commit('prefs/setColorScheme', JSON.parse(storageEv.newValue))
}
})
+7
View File
@@ -1,4 +1,7 @@
import Dexie from 'dexie'
import { initializeDexieTables } from 'libeds/dexiesync'
import { EDS } from '@/plugins/eds'
export const PMDB = new Dexie('PipedMaterialDB')
@@ -6,6 +9,10 @@ PMDB.version(3).stores({
watchedVideos: '++id,videoId,progressPcnt,timestamp'
})
export async function syncPMDB () {
return initializeDexieTables({ EDS, tableList: [PMDB.watchedVideos] })
}
export async function addWatchedVideo (videoObj) {
return PMDB.watchedVideos.add({
videoId: videoObj.videoId,
+795 -39
View File
File diff suppressed because it is too large Load Diff