Skip to content

Commit

Permalink
feature/add_axios (#53)
Browse files Browse the repository at this point in the history
  • Loading branch information
mlyubimov authored Nov 22, 2023
1 parent 206c8f9 commit 59602b6
Show file tree
Hide file tree
Showing 11 changed files with 248 additions and 206 deletions.
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
},
"dependencies": {
"@quasar/extras": "^1.14.0",
"axios": "^1.6.2",
"core-js": "^3.6.5",
"d3": "^7.8.5",
"loglevel": "^1.8.0",
Expand Down
1 change: 1 addition & 0 deletions frontend/quasar.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ module.exports = configure(function (ctx) {
// --> boot files are part of "main.js"
// https://quasar.dev/quasar-cli/boot-files
boot: [
'axios'
],

// https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-css
Expand Down
34 changes: 34 additions & 0 deletions frontend/src/boot/axios.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { boot } from 'quasar/wrappers'
import axios from 'axios'

let API_ENDPOINT = process.env.ARCHIVARIUS_API_ENDPOINT
if (localStorage.getItem('catalogChannel') !== null) {
if (localStorage.getItem('catalogChannel') === 'production') {
API_ENDPOINT = 'https://catalog.flipperzero.one/api/v0'
} else {
API_ENDPOINT = 'https://catalog.flipp.dev/api/v0'
}
}

const api = axios.create({
baseURL: API_ENDPOINT,
timeout: 25000,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
})

export default boot(({ app }) => {
// for use inside Vue files (Options API) through this.$axios and this.$api

app.config.globalProperties.$axios = axios
// ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
// so you won't necessarily have to import axios in each vue file

app.config.globalProperties.$api = api
// ^ ^ ^ this will allow you to use this.$api (for Vue Options API form)
// so you can easily perform requests against your app's API
})

export { axios, api }
2 changes: 1 addition & 1 deletion frontend/src/components/SearchBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@

<script setup>
import { ref, watch, computed } from 'vue'
import { fetchAppsShort } from '../util/util'
import { fetchAppsShort } from 'util/fetch'
import { useAppsStore } from 'src/stores/apps'
const appsStore = useAppsStore()
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/components/Updater.vue
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@
<script setup>
import { ref, defineEmits, watch, onMounted, computed } from 'vue'
import ProgressBar from './ProgressBar.vue'
import { fetchChannels, fetchFirmware, fetchRegions, unpack } from '../util/util'
import { unpack } from 'util/util'
import { fetchChannels, fetchFirmware, fetchRegions } from 'util/fetch'
import semver from 'semver'
import asyncSleep from 'simple-async-sleep'
import { PB } from '../flipper-js/protobufCompiled'
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/layouts/AppsLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@
import { onMounted, computed, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useQuasar } from 'quasar'
import { fetchCategories, fetchAppsShort, fetchAppById } from 'util/util'
import { fetchCategories, fetchAppsShort, fetchAppById } from 'util/fetch'
import SearchBar from 'components/SearchBar.vue'
import { log } from 'composables/useLog'
import { rpcErrorHandler } from 'composables/useRpcUtils'
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/pages/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,8 @@

<script setup>
import { onUpdated, defineEmits, ref, computed, watch, onUnmounted } from 'vue'
import { bytesToSize, submitAppReport } from '../util/util'
import { bytesToSize } from 'util/util'
import { submitAppReport } from 'util/fetch'
import { useMainStore } from 'stores/main'
const mainStore = useMainStore()
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/pages/Device.vue
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue'
import Updater from 'components/Updater.vue'
import asyncSleep from 'simple-async-sleep'
import { bytesToSize } from '../util/util'
import { bytesToSize } from 'util/util'
import { log } from 'composables/useLog'
import { rpcErrorHandler } from 'composables/useRpcUtils'
Expand Down Expand Up @@ -258,15 +258,15 @@ const stopScreenStream = async () => {
flags.value.screenStream = false
}
const onUpdateStage = (stage) => {
mainStore.update(stage)
mainStore.onUpdateStage(stage)
if (stage === 'start') {
flags.value.updateInProgress = true
stopScreenStream()
navigator.serial.addEventListener('connect', () => {
mainStore.update('end')
mainStore.onUpdateStage('end')
})
} else if (stage === 'end') {
mainStore.update('end')
mainStore.onUpdateStage('end')
}
}
Expand Down
12 changes: 6 additions & 6 deletions frontend/src/stores/apps.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { useRouter } from 'vue-router'
import { fetchAppsVersions, fetchAppFap } from 'util/util'
import { fetchAppsVersions, fetchAppFap } from 'util/fetch'
import asyncSleep from 'simple-async-sleep'
import { log } from 'composables/useLog'
import showNotif from 'composables/useShowNotif'
import useSetProperty from 'composables/useSetProperty'
import { rpcErrorHandler } from 'composables/useRpcUtils'
import { axios } from 'boot/axios'

import { useMainStore } from 'stores/main'

Expand Down Expand Up @@ -270,13 +271,12 @@ export const useAppsStore = defineStore('apps', () => {
})

// generate manifest
function urlContentToDataUri (url) {
return fetch(url)
.then(response => response.blob())
.then(blob => new Promise(resolve => {
async function urlContentToDataUri (url) {
return await axios.get(url, { responseType: 'blob' })
.then(({ data }) => new Promise(resolve => {
const reader = new FileReader()
reader.onload = function () { resolve(this.result) }
reader.readAsDataURL(blob)
reader.readAsDataURL(data)
}))
}
const dataUri = await urlContentToDataUri(app.currentVersion.iconUri)
Expand Down
195 changes: 195 additions & 0 deletions frontend/src/util/fetch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import semver from 'semver'
import { axios, api } from 'boot/axios'
import { camelCaseDeep, unpack } from 'util/util'

async function fetchChannels (target) {
return await axios
.get('https://update.flipperzero.one/firmware/directory.json')
.then(({ data }) => {
const release = data.channels.find((e) => e.id === 'release')
const rc = data.channels.find((e) => e.id === 'release-candidate')
const dev = data.channels.find((e) => e.id === 'development')
const params = new URLSearchParams(location.search)
const customSource = {
url: params.get('url'),
channel: params.get('channel'),
version: params.get('version'),
target: params.get('target')
}

function formatChannel (channel) {
channel.versions.sort((a, b) => {
if (semver.lt(a.version, b.version)) return 1
else return -1
})
const output = {
version: '',
date: '',
url: '',
files: [],
changelog: ''
}
const updater = channel.versions[0].files.find(
(file) => file.target === 'f' + target && file.type === 'update_tgz'
)
if (updater) {
output.url = updater.url
}
output.version = channel.versions[0].version
output.date = new Date(channel.versions[0].timestamp * 1000)
.toISOString()
.slice(0, 10)
output.files = channel.versions[0].files.sort((a, b) => {
if (a.url.match(/[\w.]+$/g)[0] > b.url.match(/[\w.]+$/g)[0]) return 1
else return -1
})
output.changelog = channel.versions[0].changelog
return output
}

const releaseChannel = formatChannel(release)
const rcChannel = formatChannel(rc)
const devChannel = formatChannel(dev)

let customChannel
if (customSource.url) {
customChannel = {
channel: customSource.channel,
version: customSource.version,
date: new Date().toISOString().slice(0, 10),
url: customSource.url,
files: [
{
url: customSource.url,
type: 'update_tgz',
target: customSource.target
}
]
}
}
return {
release: releaseChannel,
rc: rcChannel,
dev: devChannel,
custom: customChannel
}
})
.catch(({ status }) => {
if (status >= 400) {
throw new Error('Failed to fetch firmware channels (' + status + ')')
}
})
}

async function fetchFirmware (url) {
return await axios
.get(url, { responseType: 'arraybuffer' })
.then(async ({ data }) => {
return unpack(data)
})
.catch(({ status }) => {
if (status >= 400) {
throw new Error('Failed to fetch resources (' + status + ')')
}
})
}

async function fetchRegions () {
return axios
.get('https://update.flipperzero.one/regions/api/v0/bundle')
.then(({ data }) => {
if (data.error) {
throw new Error(data.error.text)
} else if (data.success) {
return data.success
}
})
.catch(({ status }) => {
if (status >= 400) {
throw new Error('Failed to fetch region (' + status + ')')
}
})
}

async function fetchCategories (params) {
return await api.get('/category', { params }).then(({ data }) => {
return data.map((category) => camelCaseDeep(category))
})
}

async function fetchAppsShort (params) {
return await api.get('/application', { params }).then(({ data }) => {
return data.map((app) => camelCaseDeep(app))
})
}

async function fetchAppById (id, params) {
if (!params.target) {
delete params.target
}
if (!params.api) {
delete params.api
}
return await api.get(`/application/${id}`, { params }).then(({ data }) => {
return camelCaseDeep(data)
})
}

async function fetchAppFap (params) {
return await api
.get(`/application/version/${params.versionId}/build/compatible`, {
params: {
target: params.target,
api: params.api
},
responseType: 'arraybuffer'
})
.then(({ data }) => {
return data
})
.catch(({ status }) => {
if (status >= 400) {
throw new Error('Failed to fetch application build (' + status + ')')
}
})
}

async function fetchAppsVersions (uids) {
const size = 100
const subUids = []

for (let i = 0; i < Math.ceil(uids.length / size); i++) {
subUids[i] = uids.slice(i * size, i * size + size)
}

const allVersions = []
for (const sliceUids of subUids) {
await api
.post('/1/application/versions', {
application_versions: sliceUids,
limit: size
})
.then(({ data }) => allVersions.push(...data))
}

const versions = allVersions.map((version) => camelCaseDeep(version))
return versions
}

async function submitAppReport (id, report) {
return api.post(`/application/${id}/issue`, {
...report
})
}

export {
fetchChannels,
fetchFirmware,
fetchRegions,
fetchCategories,
fetchAppsShort,
fetchAppById,
fetchAppFap,
fetchAppsVersions,
submitAppReport
}
Loading

0 comments on commit 59602b6

Please sign in to comment.