Skip to content

Commit

Permalink
Add toggler for switch data sources
Browse files Browse the repository at this point in the history
  • Loading branch information
nkokla committed Apr 22, 2024
1 parent df84041 commit ff1671b
Show file tree
Hide file tree
Showing 4 changed files with 394 additions and 200 deletions.
41 changes: 22 additions & 19 deletions .env.default
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
NEXT_PUBLIC_ADRESSE_URL=http://localhost:3000

# --------------------------------------------------------------------------------------------------------------
# Parametres URL Externe autre projet BAN
# ---------------------------------------------------------------------------------------------------------------
# -----------------------------------------------
# --- Parametres URL Externe autre projet BAN ---
# -----------------------------------------------
NEXT_PUBLIC_API_GEO_URL=https://geo.api.gouv.fr/2023
NEXT_PUBLIC_API_ETABLISSEMENTS_PUBLIC=https://api-lannuaire.service-public.fr/api/explore/v2.1

NEXT_PUBLIC_API_BAN_URL=https://plateforme.adresse.data.gouv.fr
NEXT_PUBLIC_API_ADRESSE=https://api-adresse.data.gouv.fr

# --------------------------------------------------------------------------------------------------------------
# Utilisation appel externe Bal
# ---------------------------------------------------------------------------------------------------------------
# -------------------------------------
# --- Utilisation appel externe Bal ---
# -------------------------------------

NEXT_PUBLIC_API_DEPOT_URL=https://plateforme-bal.adresse.data.gouv.fr/api-depot
API_DEPOT_TOKEN=
Expand All @@ -28,29 +28,32 @@ NEXT_PUBLIC_BAL_ADMIN_API_URL=https://bal-admin.adresse.data.gouv.fr/api
NEXT_PUBLIC_BAL_API_URL=https://api-bal.adresse.data.gouv.fr/v1
NEXT_PUBLIC_MES_ADRESSES=https://mes-adresses.data.gouv.fr

# --------------------------------------------------------------------------------------------------------------
# Utilisation Matomo pour les stats
# ---------------------------------------------------------------------------------------------------------------
# -----------------------------------------
# --- Utilisation Matomo pour les stats ---
# -----------------------------------------
NEXT_PUBLIC_MATOMO_URL=
NEXT_PUBLIC_MATOMO_SITE_ID=
MATOMO_TOKEN_AUTH=
# The Matomo Token is actualy not required
# MATOMO_TOKEN_AUTH=

# A voir pour supprimer cette variable qui utilise la lib helmet
ENABLE_HELMET=0

# Defintion du port
# Defintion du port
PORT=3000

# Valeur 0 ou 1 pour la demande de certification
NEXT_PUBLIC_CERTIFICAT_NUMEROTATION_ENABLED=

PATH_STATIC_FILE=
# --------------------------------------
# --- Connexion au S3 pour les datas ---
# --------------------------------------

# --------------------------------------------------------------------------------------------------------------
# Connexion au S3 pour les datas
# ---------------------------------------------------------------------------------------------------------------
S3_CONFIG_ACCESS_KEY_ID=
S3_CONFIG_SECRET_ACCESS_KEY=
S3_CONFIG_REGION=
S3_CONFIG_ENDPOINT=

S3_CONFIG_ACCESS_KEY_ID =
S3_CONFIG_SECRET_ACCESS_KEY =
S3_CONFIG_REGION =
S3_CONFIG_ENDPOINT =
# Connexion au Dossier Data local (en cas de rollback - TOGGLER_DATA_SOURCES=local sinon TOGGLER_DATA_SOURCES=S3)
TOGGLER_DATA_SOURCES=S3
PATH_STATIC_FILE=
188 changes: 7 additions & 181 deletions pages/data/[[...path]].js
Original file line number Diff line number Diff line change
@@ -1,183 +1,9 @@
/* eslint-disable operator-linebreak */
import PropTypes from 'prop-types'
import {Download} from 'react-feather'
import {S3} from '@aws-sdk/client-s3'
import {fr} from '@codegouvfr/react-dsfr'
import DataPageLegacy, {getServerSideProps as getServerSidePropsLegacy} from './data-legacy'
import DataPageS3, {getServerSideProps as getServerSidePropsS3} from './data-s3'

import Head from '@/components/head'
import Section from '@/components/section'
import Page from '@/layouts/main'
import sendToTracker, {getDownloadToEventTracker} from '@/lib/util/analytics-tracker'
import Data, {
config as dataConfig,
getAlias,
getFormatedDate,
asyncSendS3,
listObjectsRecursively,
autorizedPathS3,
} from '@/views/data'
const {TOGGLER_DATA_SOURCES} = process.env
const getServerSideProps = TOGGLER_DATA_SOURCES === 'S3' ? getServerSidePropsS3 : getServerSidePropsLegacy
const DataPage = TOGGLER_DATA_SOURCES === 'S3' ? DataPageS3 : DataPageLegacy

import ErrorPage from '../_error'

const {
S3_CONFIG_ACCESS_KEY_ID,
S3_CONFIG_SECRET_ACCESS_KEY,
S3_CONFIG_REGION,
S3_CONFIG_ENDPOINT,
} = process.env

const rootLink = {
href: '/donnees-nationales',
label: 'Données nationales',
}

const bucketName = 'prd-ign-mut-ban'
const rootDir = ['adresse-data']
const clientS3 = new S3({
credentials: {
accessKeyId: S3_CONFIG_ACCESS_KEY_ID,
secretAccessKey: S3_CONFIG_SECRET_ACCESS_KEY,
},
region: S3_CONFIG_REGION,
endpoint: S3_CONFIG_ENDPOINT,
})

export async function getServerSideProps(context) {
const {params, res, req} = context
const {path: paramPathRaw = []} = params
const config = dataConfig?.directory.find(({path}) => path === paramPathRaw.join('/')) || {}
const alias = await getAlias(clientS3, bucketName)(rootDir, dataConfig?.alias, paramPathRaw.join('/') || '')

const paramPath = alias && (new RegExp(`^${alias.parent}/${alias.name}`)).test(paramPathRaw.join('/'))
? paramPathRaw.join('/').replace(new RegExp(`^${alias.parent}/${alias.name}`), `${alias.parent}/${alias.target}`).split('/')
: paramPathRaw
const dirPath = `${paramPath.join('/')}`
const formattedDate = getFormatedDate()
const s3ObjectPath = [...rootDir, ...paramPath].join('/')

try {
const s3Head = await clientS3.headObject({
Bucket: bucketName,
Key: s3ObjectPath,
})

// NO ERROR > PATH IS A FILE
try {
sendToTracker(getDownloadToEventTracker({
downloadDataType: `${paramPath[0]}${req?.headers?.range ? ' (Partial)' : ''}`,
downloadFileName: dirPath,
nbDownload: 1
}))
await asyncSendS3(clientS3)(req, res, {
params: {
Bucket: bucketName,
Key: s3ObjectPath,
},
fileName: paramPath[paramPath.length - 1],
metadata: s3Head,
})
} catch (err) {
console.warn(`[${formattedDate} - ERROR]`, 'File access error:', err)
return {
props: {
errorCode: 502,
errorMessage: 'Une erreur est survenue lors de la génération du téléchargement.',
},
}
}
} catch {
// ERROR > PATH SHOULD BE DIRECTORY
const s3DirPath = `${s3ObjectPath}/`
const s3Objects = await listObjectsRecursively(clientS3, bucketName)(s3DirPath)
if (s3Objects) {
const s3data = [
...(s3Objects || [])
.filter(({name}) => autorizedPathS3(name).auth)
.map(({name, path, isDirectory, fileInfo, ...rest}) => ({
name,
path: alias && alias.parent !== dirPath
? path.replace(
(new RegExp(`^${rootDir.join('/')}/${alias.parent}/${alias.target}`)),
`${rootDir.join('/')}/${alias.parent}/${alias.name}`
)
: path,
isDirectory,
...(fileInfo ? {fileInfo} : {}),
...rest,
}))
]
const s3contentDir = [
...s3data,
...(alias && alias.parent === dirPath ? [{
...alias,
...(s3data.find(({name}) => name === alias.target) || {}),
name: alias.name,
path: `${s3ObjectPath}/${alias.name}`,
}] : []),
].sort((a, b) => a.name.localeCompare(b.name) || a.isDirectory - b.isDirectory)

return {
props: {
title: ['data', ...paramPath].join('/') || '',
path: paramPathRaw || [],
data: s3contentDir || [],
config,
}
}
}

// OBJECT DO NOT EXIST > IS UNKNOWN PATH
console.warn(`[${formattedDate} - ERROR]`, 'S3 Object - Try access to unknown object:', s3DirPath)
res.statusCode = 404
return {
props: {errorCode: 404},
}
}

return {props: {}}
}

export default function DataPage({title, path, data, config, errorCode, errorMessage}) {
const {hero} = config
return errorCode && errorCode !== 200 ?
(<ErrorPage code={errorCode} message={errorMessage} />) :
(path ? (
<Page title={`BAN OpenData : ${title}`}>
<Head
title='Téléchargez les données de la BAN'
icon={<Download size={56} alt='' aria-hidden='true' />}
/>
<Section>
{hero && (
<p>
{hero.type === 'warning' && <i className={fr.cx('fr-icon-warning-fill', 'warn-icon')} />}
{hero.value}
</p>
)}
<Data {...{root: rootLink, path, data, config}} />
</Section>

<style jsx>{`
.warn-icon {
color: #f60700;
float: left;
margin: 0 0.5em 0 0;
}
`}</style>
</Page>
) : null)
}

DataPage.propTypes = {
title: PropTypes.string,
path: PropTypes.arrayOf(PropTypes.string),
data: PropTypes.arrayOf(PropTypes.object),
config: PropTypes.shape({
hero: PropTypes.shape({
type: PropTypes.string,
value: PropTypes.string,
}),
}),
errorCode: PropTypes.number,
errorMessage: PropTypes.string,
}
export {getServerSideProps}
export default DataPage
Loading

0 comments on commit ff1671b

Please sign in to comment.