Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • hub/absd
1 result
Show changes
Commits on Source (6)
......@@ -63,9 +63,9 @@ spec:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 5
initialDelaySeconds: 0
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 5
resources:
requests:
......
......@@ -69,5 +69,8 @@ export default Object.freeze({
},
archiveURL: (archiveName: string): string => {
return `/api/downloads/${archiveName}`
},
databaseIsReady: async (): Promise<AxiosResponse<any>> => {
return await base.get('/api/readyDatabase')
}
})
......@@ -14,14 +14,23 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import { useStore } from '../store'
import AppFooter from './AppFooter.vue'
import AppHeader from './AppHeader.vue'
import router from '../router'
import api from '../api'
import { AxiosError } from 'axios'
const store = useStore()
onMounted(() => {
store.getStatistics()
onMounted(async () => {
await api.databaseIsReady()
.then()
.catch((err: AxiosError) => {
if (err.status === 503) {
router.replace({ name: 'WaitingPage' })
} else {
alert(err.message)
console.log(err)
}
})
})
</script>
......
<template>
<h1>
<img
src="/absd-title.png"
alt="ABSD logo with title"
height="90"
width="325">
<span class="title-for-a11y">
ABSD - AntiBody Sequence database
</span>
</h1>
</template>
<script setup lang="ts"></script>
<style scoped>
.title-for-a11y {
display: none;
}
</style>
<template>
<h1>404 - Not Found</h1>
<p>The page or resource you requested doesn't seem to exist here...</p>
<AppTitle></AppTitle>
<section>
<h2>404 - Not Found</h2>
<p>The page or resource you requested doesn't seem to exist here...</p>
<RouterLink :to="{ name: 'HomePage' }">
Go back home
</RouterLink>
</section>
</template>
<script setup lang="ts">
import AppTitle from './AppTitle.vue'
</script>
<style scoped>
section {
display: flex;
flex-flow: column;
align-items: center;
gap: var(--spacing);
}
h2,
p {
text-align: center;
}
p {
a, p {
font-size: 20px;
margin-top: 0;
}
</style>
<template>
<h1>
<img
src="/absd-title.png"
alt="ABSD logo with title"
height="90"
width="325"
>
<span class="title-for-a11y">
ABSD - AntiBody Sequence database
</span>
</h1>
<AppTitle></AppTitle>
<section class="page-description">
<p>
......@@ -141,6 +131,7 @@
<script setup lang="ts">
import { computed, onMounted } from 'vue'
import { useStore } from '../store'
import AppTitle from './AppTitle.vue'
import AntibodiesTable from './AntibodiesTable.vue'
import NoResults from './NoResults.vue'
import SearchBar from './SearchBar.vue'
......@@ -154,8 +145,10 @@ const versionDate = computed(() => { return store.antibodiesVersionDate })
* give the user a preview of what to expect after a search.
*/
onMounted((): void => {
store.fetchAntibodies(store.request.fetchParams())
store.countAntibodies(store.request.countParams())
store.getStatistics().then(() => {
store.fetchAntibodies(store.request.fetchParams())
store.countAntibodies(store.request.countParams())
})
})
</script>
......
<template>
<AppTitle></AppTitle>
<section>
<p>
The Database is currently being updated,
please come back in a few minutes.
</p>
<div class="spinner"></div>
</section>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue'
import AppTitle from './AppTitle.vue'
import api from '../api'
import { AxiosError } from 'axios'
import router from '../router'
/** ID of the interval timer started on page load */
let intervalID: number
/**
* After page load, check for database readiness immediately
* and every 5 seconds.
*/
onMounted(() => {
checkDatabaseIsReady()
intervalID = setInterval(checkDatabaseIsReady, 5000)
})
/**
* Remove the timer when leaving the page to ensure
* memory safety.
*/
onUnmounted(() => {
clearInterval(intervalID)
})
/**
* Check is the database is ready and reroute to
* the home page in that case.
*/
function checkDatabaseIsReady() {
api.databaseIsReady()
.then(() => {
router.replace({ name: 'HomePage' })
})
.catch((err: AxiosError) => {
if (err.status === 503) return
else {
alert(err.message)
console.log(err)
}
})
}
</script>
<style scoped>
section {
align-items: center;
display: flex;
flex-flow: column wrap;
gap: var(--spacing);
}
.spinner {
height: 50px;
width: 50px;
}
</style>
......@@ -51,6 +51,11 @@ export default createRouter({
component: async () => await import('./components/TheAboutPage.vue'),
name: 'AboutPage'
},
{
path: '/waiting',
component: async () => await import('./components/TheWaitingPage.vue'),
name: 'WaitingPage'
},
{
path: '/:pathMatch(.*)*',
component: async () => await import('./components/The404Page.vue'),
......
......@@ -170,8 +170,8 @@ export const useStore = defineStore('store', {
* Get the database statistics from the server and stores it in the
* corresponding variable in the store.
*/
getStatistics(): void {
api.getStatistics().then(response => {
getStatistics(): Promise<void> {
return api.getStatistics().then(response => {
this.statistics = Object.freeze(response.data)
}).catch((err: AxiosError) => {
alert(err.message)
......
......@@ -36,6 +36,7 @@ import downloadsFileRoute from './routes/downloadsFileRoute.js'
import downloadsRoute from './routes/downloadsRoute.js'
import healthCheckRoute from './routes/healthCheckRoute.js'
import readyCheckRoute from './routes/readyCheckRoute.js'
import readyDatabaseRoute from './routes/readyDatabaseRoute.js'
import statisticsRoute from './routes/statisticsRoute.js'
/**
......@@ -86,9 +87,10 @@ fastify.register(fastifyMongo, {
// Route controllers
// =========================================================================
// Decorate the server with a boolean marking when it is ready.
// Used by the readyCheck route.
// Decorate the server with booleans marking when resources are ready.
// Used by the readiness routes.
fastify.decorate('serverIsReady', false)
fastify.decorate('databaseIsReady', true)
// Decorate the server with an object
// to store the workers while then run.
......@@ -101,7 +103,7 @@ fastify.addHook('onRequest', async (request) => {
request.where = {}
})
// Register all controllers
// Register all API controllers
fastify.route(antibodiesCountRoute)
fastify.route(antibodiesDownloadArchiveFileRoute)
fastify.route(antibodiesDownloadArchiveRoute)
......@@ -114,6 +116,9 @@ fastify.route(antibodiesVGeneSegmentsRoute)
fastify.route(downloadsFileRoute)
fastify.route(downloadsRoute)
fastify.route(statisticsRoute)
fastify.route(readyDatabaseRoute)
// Register health checks
fastify.route(healthCheckRoute)
fastify.route(readyCheckRoute)
......@@ -164,15 +169,19 @@ fastify.listen({
host: envConfig.ABSD_SERVER_HOST,
port: envConfig.ABSD_SERVER_PORT
}).then(async () => {
fastify.serverIsReady = true
fastify.log.info({ envConfig })
if (envConfig.NODE_ENV === 'production') {
fastify.databaseIsReady = false
return await buildDatabase()
}
}).then((dbResults) => {
if (dbResults) {
fastify.log.info(`${envConfig.ABSD_DB_NAME} database created`)
}
fastify.log.info({ envConfig })
fastify.serverIsReady = true
fastify.databaseIsReady = true
}).catch(err => {
fastify.log.error(err)
process.exit(1)
......
// ABSD
// Copyright (C) 2025 Institut Pasteur
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/**
* Route returning if the database is available or not.
* False when the database is inserting data.
*/
export default {
method: 'GET',
url: '/api/readyDatabase',
logLevel: 'warn',
handler: function (request, reply) {
if (this.databaseIsReady) {
return reply
.code(200)
.send({ ready: true })
}
return reply
.code(503)
.send({ ready: false })
}
}