mirror of
https://github.com/Paris-est-Ludique/intranet.git
synced 2025-06-08 08:34:20 +02:00
🚧 WIP
This commit is contained in:
parent
74685fa741
commit
42c7410bfe
@ -8,7 +8,3 @@ GSHEET_ID=
|
|||||||
DISCORD_TOKEN=
|
DISCORD_TOKEN=
|
||||||
DISCORD_CLIENTID=
|
DISCORD_CLIENTID=
|
||||||
DISCORD_GUILDID=
|
DISCORD_GUILDID=
|
||||||
|
|
||||||
## Notifications
|
|
||||||
FORCE_ORANGE_PUBLIC_VAPID_KEY=
|
|
||||||
FORCE_ORANGE_PRIVATE_VAPID_KEY=
|
|
11
.eslintrc.js
11
.eslintrc.js
@ -55,11 +55,10 @@ module.exports = {
|
|||||||
"jsx-a11y/control-has-associated-label": "off",
|
"jsx-a11y/control-has-associated-label": "off",
|
||||||
},
|
},
|
||||||
globals: {
|
globals: {
|
||||||
__CLIENT__: true,
|
API_URL: false,
|
||||||
__SERVER__: true,
|
SSR: true,
|
||||||
__DEV__: true,
|
DEV: true,
|
||||||
__LOCAL__: false,
|
REGISTER_DISCORD_COMMANDS: false,
|
||||||
__REGISTER_DISCORD_COMMANDS__: false,
|
TEST: false,
|
||||||
__TEST__: false,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,6 @@ COPY . .
|
|||||||
## Build the app
|
## Build the app
|
||||||
RUN yarn run build
|
RUN yarn run build
|
||||||
|
|
||||||
EXPOSE 8080
|
EXPOSE 4000
|
||||||
|
|
||||||
CMD ["yarn", "start"]
|
CMD ["yarn", "run", "start"]
|
@ -54,7 +54,7 @@ Now the app should be serving at <http://localhost:3000>.
|
|||||||
## Using Docker
|
## Using Docker
|
||||||
|
|
||||||
`docker build . -t force-orange`
|
`docker build . -t force-orange`
|
||||||
`docker run -d -p 3000:8080 -t force-orange`
|
`docker run -d -p 3000:3000 -t force-orange`
|
||||||
|
|
||||||
### Commands
|
### Commands
|
||||||
|
|
||||||
|
@ -18,12 +18,11 @@ module.exports = {
|
|||||||
"<rootDir>/jest/assetMock.ts",
|
"<rootDir>/jest/assetMock.ts",
|
||||||
},
|
},
|
||||||
globals: {
|
globals: {
|
||||||
__DEV__: true,
|
API_URL: "http://localhost:3000",
|
||||||
__CLIENT__: true,
|
DEV: true,
|
||||||
__SERVER__: false,
|
SSR: false,
|
||||||
__LOCAL__: false,
|
REGISTER_DISCORD_COMMANDS: false,
|
||||||
__REGISTER_DISCORD_COMMANDS__: false,
|
TEST: true,
|
||||||
__TEST__: true,
|
|
||||||
},
|
},
|
||||||
maxConcurrency: 50,
|
maxConcurrency: 50,
|
||||||
maxWorkers: 1,
|
maxWorkers: 1,
|
||||||
|
@ -43,9 +43,9 @@
|
|||||||
"dev": "yarn dev:build && nodemon -r dotenv/config ./public/server",
|
"dev": "yarn dev:build && nodemon -r dotenv/config ./public/server",
|
||||||
"ser": "yarn dev:build && node -r dotenv/config ./public/server",
|
"ser": "yarn dev:build && node -r dotenv/config ./public/server",
|
||||||
"dev:build": "cross-env NODE_ENV=development webpack --config ./webpack/server.config.ts",
|
"dev:build": "cross-env NODE_ENV=development webpack --config ./webpack/server.config.ts",
|
||||||
"local-start": "cross-env LOCAL=true yarn build && node ./public/server",
|
"local-start": "cross-env LOCAL=true yarn build && node -r dotenv/config ./public/server",
|
||||||
"discord-register": "cross-env REGISTER_DISCORD_COMMANDS=true yarn build && node ./public/server",
|
"discord-register": "cross-env REGISTER_DISCORD_COMMANDS=true yarn build && node -r dotenv/config ./public/server",
|
||||||
"start": "node ./public/server",
|
"start": "node -r dotenv/config ./public/server",
|
||||||
"build": "run-s build:*",
|
"build": "run-s build:*",
|
||||||
"build:server": "cross-env NODE_ENV=production webpack --config ./webpack/server.config.ts",
|
"build:server": "cross-env NODE_ENV=production webpack --config ./webpack/server.config.ts",
|
||||||
"build:client": "cross-env NODE_ENV=production webpack --config ./webpack/client.config.ts",
|
"build:client": "cross-env NODE_ENV=production webpack --config ./webpack/client.config.ts",
|
||||||
@ -82,8 +82,6 @@
|
|||||||
"@types/js-cookie": "^3.0.1",
|
"@types/js-cookie": "^3.0.1",
|
||||||
"@types/lodash": "^4.14.177",
|
"@types/lodash": "^4.14.177",
|
||||||
"@types/react-modal": "^3.13.1",
|
"@types/react-modal": "^3.13.1",
|
||||||
"@types/serviceworker": "^0.0.32",
|
|
||||||
"@types/web-push": "^3.3.2",
|
|
||||||
"autoprefixer": "^10.2.6",
|
"autoprefixer": "^10.2.6",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"bcrypt": "^5.0.1",
|
"bcrypt": "^5.0.1",
|
||||||
@ -127,7 +125,6 @@
|
|||||||
"redux-thunk": "^2.4.2",
|
"redux-thunk": "^2.4.2",
|
||||||
"serialize-javascript": "^6.0.0",
|
"serialize-javascript": "^6.0.0",
|
||||||
"serve-favicon": "^2.5.0",
|
"serve-favicon": "^2.5.0",
|
||||||
"web-push": "^3.4.5",
|
|
||||||
"xml2js": "^0.4.23"
|
"xml2js": "^0.4.23"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
45
scripts/deploy.sh
Normal file
45
scripts/deploy.sh
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script from :
|
||||||
|
# https://emilwypych.com/2020/10/25/how-to-run-discord-bot-on-cloud-run/?cn-reloaded=1
|
||||||
|
|
||||||
|
GCP_CONFIGURATION_NAME=barajeuxonline
|
||||||
|
GCP_PROJECT_NAME=barajeuxonline
|
||||||
|
GCP_REGION=europe-west1
|
||||||
|
PROJECT_NAME=barajeuxonline-bot
|
||||||
|
|
||||||
|
if [ -z "$1" ]
|
||||||
|
then
|
||||||
|
ENVSUFFIX="test"
|
||||||
|
else
|
||||||
|
ENVSUFFIX=$1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure proper GCP configuration is set
|
||||||
|
echo "[GCloud] get configuration"
|
||||||
|
gcloud config configurations activate ${GCP_CONFIGURATION_NAME}
|
||||||
|
|
||||||
|
# rm data/auth.json
|
||||||
|
# cp data/auth-"${ENVSUFFIX}".json data/auth.json
|
||||||
|
echo "[Docker] Build a new docker image"
|
||||||
|
|
||||||
|
docker build --no-cache -t gcr.io/${GCP_PROJECT_NAME}/"${PROJECT_NAME}-${ENVSUFFIX}" .
|
||||||
|
|
||||||
|
echo "[Docker] Push the new docker image"
|
||||||
|
docker push gcr.io/${GCP_PROJECT_NAME}/"${PROJECT_NAME}-${ENVSUFFIX}"
|
||||||
|
|
||||||
|
echo "[GCloud] Deploy new revision of ${PROJECT_NAME}-${ENVSUFFIX}"
|
||||||
|
|
||||||
|
gcloud run deploy "${PROJECT_NAME}-${ENVSUFFIX}" --image=gcr.io/${GCP_PROJECT_NAME}/"${PROJECT_NAME}-${ENVSUFFIX}" \
|
||||||
|
--platform=managed --region=${GCP_REGION} --allow-unauthenticated \
|
||||||
|
--max-instances 1 --memory=2Gi --cpu=2
|
||||||
|
|
||||||
|
echo "[GCloud] Ensure that there is cron job for checking ${PROJECT_NAME}-${ENVSUFFIX}"
|
||||||
|
|
||||||
|
# Get proper URL
|
||||||
|
GCP_APP_URL=$(gcloud run services list --platform=managed --region=${GCP_REGION} \
|
||||||
|
--filter="status.address.url ~ ${PROJECT_NAME}-${ENVSUFFIX}" \
|
||||||
|
--format="value(status.address.url)")
|
||||||
|
|
||||||
|
gcloud scheduler jobs create http GET-"${PROJECT_NAME}-${ENVSUFFIX}" \
|
||||||
|
--schedule="* * * * *" --uri="${GCP_APP_URL}" --http-method GET
|
@ -2,7 +2,6 @@ import { RouteConfig, renderRoutes } from "react-router-config"
|
|||||||
import { Helmet } from "react-helmet"
|
import { Helmet } from "react-helmet"
|
||||||
import { ToastContainer } from "react-toastify"
|
import { ToastContainer } from "react-toastify"
|
||||||
|
|
||||||
import config from "../config"
|
|
||||||
// Import your global styles here
|
// Import your global styles here
|
||||||
import "normalize.css/normalize.css"
|
import "normalize.css/normalize.css"
|
||||||
import "react-toastify/dist/ReactToastify.css"
|
import "react-toastify/dist/ReactToastify.css"
|
||||||
@ -17,6 +16,19 @@ interface Route {
|
|||||||
|
|
||||||
export const reactAppId = "react-view"
|
export const reactAppId = "react-view"
|
||||||
|
|
||||||
|
const app = {
|
||||||
|
htmlAttributes: { lang: "en" },
|
||||||
|
title: "Force Orange",
|
||||||
|
description: "Le site des bénévoles",
|
||||||
|
titleTemplate: "Force Orange - %s",
|
||||||
|
meta: [
|
||||||
|
{
|
||||||
|
name: "description",
|
||||||
|
content: "The best react universal starter boilerplate in the world.",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
const App = ({ route, location }: Route): JSX.Element => {
|
const App = ({ route, location }: Route): JSX.Element => {
|
||||||
if (location.pathname === "/fiches") {
|
if (location.pathname === "/fiches") {
|
||||||
@ -26,7 +38,7 @@ const App = ({ route, location }: Route): JSX.Element => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Helmet {...config.APP}>
|
<Helmet {...app}>
|
||||||
<meta
|
<meta
|
||||||
name="viewport"
|
name="viewport"
|
||||||
content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no"
|
content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no"
|
||||||
@ -36,9 +48,9 @@ const App = ({ route, location }: Route): JSX.Element => {
|
|||||||
<div className={styles.logo} />
|
<div className={styles.logo} />
|
||||||
<div>
|
<div>
|
||||||
<h1 className={styles.siteName}>
|
<h1 className={styles.siteName}>
|
||||||
<a href="/">{config.APP.title}</a>
|
<a href="/">Force Orange</a>
|
||||||
</h1>
|
</h1>
|
||||||
<div className={styles.siteDescription}>{config.APP.description}</div>
|
<div className={styles.siteDescription}>Le site des bénévoles</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.menuWrapper}>
|
<div className={styles.menuWrapper}>
|
||||||
<MainMenu />
|
<MainMenu />
|
||||||
|
@ -1,229 +0,0 @@
|
|||||||
import _ from "lodash"
|
|
||||||
import React, { useCallback, useEffect, useRef, useState } from "react"
|
|
||||||
import isNode from "detect-node"
|
|
||||||
import { fetchVolunteerAsksSet } from "../../store/volunteerAsksSet"
|
|
||||||
import styles from "./styles.module.scss"
|
|
||||||
import { useAskTools, addAsk } from "./utils"
|
|
||||||
import FormButton from "../Form/FormButton/FormButton"
|
|
||||||
import { toastError, toastSuccess } from "../../store/utils"
|
|
||||||
|
|
||||||
export function AskPushNotif(asks: JSX.Element[], id: number): void {
|
|
||||||
const { dispatch, jwtToken, volunteerAsks } = useAskTools()
|
|
||||||
|
|
||||||
const [acceptsNotifs, setAcceptsNotifs] = useState("")
|
|
||||||
|
|
||||||
const mounted = useRef(false)
|
|
||||||
useEffect(() => {
|
|
||||||
if (mounted.current) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
mounted.current = true
|
|
||||||
if (!isNode) {
|
|
||||||
if (volunteerAsks?.acceptsNotifs === "oui") {
|
|
||||||
navigator.serviceWorker.ready.then((registration) =>
|
|
||||||
registration.pushManager.getSubscription().then((existedSubscription) => {
|
|
||||||
const doesAcceptNotifs =
|
|
||||||
_.isEqual(
|
|
||||||
JSON.parse(JSON.stringify(existedSubscription)),
|
|
||||||
JSON.parse(volunteerAsks?.pushNotifSubscription)
|
|
||||||
) && volunteerAsks?.acceptsNotifs === "oui"
|
|
||||||
setAcceptsNotifs(doesAcceptNotifs ? "oui" : "non")
|
|
||||||
})
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
setAcceptsNotifs("non")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const onChangeValue = (e: React.ChangeEvent<HTMLInputElement>) =>
|
|
||||||
setAcceptsNotifs(e.target.value)
|
|
||||||
|
|
||||||
const onSubmit = useCallback(async (): Promise<void> => {
|
|
||||||
if (isNode) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!("serviceWorker" in navigator)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (acceptsNotifs === "non") {
|
|
||||||
dispatch(
|
|
||||||
fetchVolunteerAsksSet(jwtToken, 0, {
|
|
||||||
hiddenAsks: [...(volunteerAsks?.hiddenAsks || []), id],
|
|
||||||
acceptsNotifs: "non",
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const registration = await navigator.serviceWorker.ready
|
|
||||||
|
|
||||||
if (!registration.pushManager) {
|
|
||||||
toastError(
|
|
||||||
"Il y a un problème avec le push manager. Il faudrait utiliser un navigateur plus récent !"
|
|
||||||
)
|
|
||||||
dispatch(
|
|
||||||
fetchVolunteerAsksSet(jwtToken, 0, {
|
|
||||||
hiddenAsks: [...(volunteerAsks?.hiddenAsks || []), id],
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const convertedVapidKey = urlBase64ToUint8Array(process.env.FORCE_ORANGE_PUBLIC_VAPID_KEY)
|
|
||||||
|
|
||||||
function urlBase64ToUint8Array(base64String?: string) {
|
|
||||||
if (!base64String) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
const padding = "=".repeat((4 - (base64String.length % 4)) % 4)
|
|
||||||
// eslint-disable-next-line
|
|
||||||
const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/")
|
|
||||||
|
|
||||||
const rawData = atob(base64)
|
|
||||||
const outputArray = new Uint8Array(rawData.length)
|
|
||||||
|
|
||||||
for (let i = 0; i < rawData.length; i += 1) {
|
|
||||||
outputArray[i] = rawData.charCodeAt(i)
|
|
||||||
}
|
|
||||||
return outputArray
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!convertedVapidKey) {
|
|
||||||
toastError("No convertedVapidKey available")
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const existedSubscription = await registration.pushManager.getSubscription()
|
|
||||||
|
|
||||||
if (existedSubscription === null) {
|
|
||||||
// No subscription detected, make a request
|
|
||||||
try {
|
|
||||||
const newSubscription = await registration.pushManager.subscribe({
|
|
||||||
applicationServerKey: convertedVapidKey,
|
|
||||||
userVisibleOnly: true,
|
|
||||||
})
|
|
||||||
// New subscription added
|
|
||||||
if (
|
|
||||||
volunteerAsks?.acceptsNotifs === "oui" &&
|
|
||||||
!subscriptionEqualsSave(
|
|
||||||
newSubscription,
|
|
||||||
volunteerAsks?.pushNotifSubscription
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
toastSuccess(
|
|
||||||
"Un autre navigateur était notifié, mais c'est maintenant celui-ci qui le sera."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(
|
|
||||||
fetchVolunteerAsksSet(jwtToken, 0, {
|
|
||||||
pushNotifSubscription: JSON.stringify(newSubscription),
|
|
||||||
acceptsNotifs: "oui",
|
|
||||||
hiddenAsks: [...(volunteerAsks?.hiddenAsks || []), id],
|
|
||||||
})
|
|
||||||
)
|
|
||||||
} catch (_e) {
|
|
||||||
if (Notification.permission !== "granted") {
|
|
||||||
toastError(
|
|
||||||
"Mince tu as bloqué les notifications pour le site des bénévoles ! En haut juste à gauche de la barre d'adresse, il y a une icone de cadenas ou de message barré sur lequel cliquer pour annuler ce blocage.",
|
|
||||||
false
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
toastError(
|
|
||||||
"Il y a eu une erreur avec l'enregistrement avec le Service Worker. Il faudrait utiliser un navigateur plus récent !"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Existed subscription detected
|
|
||||||
if (
|
|
||||||
volunteerAsks?.acceptsNotifs === "oui" &&
|
|
||||||
!subscriptionEqualsSave(
|
|
||||||
existedSubscription,
|
|
||||||
volunteerAsks?.pushNotifSubscription
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
toastSuccess(
|
|
||||||
"Un autre navigateur était notifié, mais c'est maintenant celui-ci qui le sera."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(
|
|
||||||
fetchVolunteerAsksSet(jwtToken, 0, {
|
|
||||||
pushNotifSubscription: JSON.stringify(existedSubscription),
|
|
||||||
acceptsNotifs: "oui",
|
|
||||||
hiddenAsks: [...(volunteerAsks?.hiddenAsks || []), id],
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} catch (_e) {
|
|
||||||
toastError(
|
|
||||||
"Il y a eu une erreur avec l'enregistrement avec le Service Worker. Il faudrait utiliser un navigateur plus récent !"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
acceptsNotifs,
|
|
||||||
dispatch,
|
|
||||||
id,
|
|
||||||
jwtToken,
|
|
||||||
volunteerAsks?.acceptsNotifs,
|
|
||||||
volunteerAsks?.hiddenAsks,
|
|
||||||
volunteerAsks?.pushNotifSubscription,
|
|
||||||
])
|
|
||||||
|
|
||||||
function subscriptionEqualsSave(toCheck: PushSubscription, save: string | undefined): boolean {
|
|
||||||
if (!save) {
|
|
||||||
return !toCheck
|
|
||||||
}
|
|
||||||
return _.isEqual(JSON.parse(JSON.stringify(toCheck)), JSON.parse(save))
|
|
||||||
}
|
|
||||||
|
|
||||||
const needToShow =
|
|
||||||
volunteerAsks?.acceptsNotifs !== "oui" && volunteerAsks?.acceptsNotifs !== "non"
|
|
||||||
|
|
||||||
addAsk(
|
|
||||||
asks,
|
|
||||||
id,
|
|
||||||
volunteerAsks,
|
|
||||||
true,
|
|
||||||
needToShow,
|
|
||||||
<div className={styles.formLine}>
|
|
||||||
<label>
|
|
||||||
Acceptes-tu de recevoir une alerte dans ton navigateur quand on en aura
|
|
||||||
d'autres à t'afficher ici ?<br />
|
|
||||||
<span className={styles.sousMessage}>
|
|
||||||
(Ça nous simplifierait la vie, on a des soucis à contacter les bénévoles par
|
|
||||||
email.)
|
|
||||||
</span>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
value="oui"
|
|
||||||
name="notifs"
|
|
||||||
checked={acceptsNotifs === "oui"}
|
|
||||||
onChange={onChangeValue}
|
|
||||||
/>{" "}
|
|
||||||
Oui
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
value="non"
|
|
||||||
name="notifs"
|
|
||||||
checked={acceptsNotifs === "non"}
|
|
||||||
onChange={onChangeValue}
|
|
||||||
/>{" "}
|
|
||||||
Non
|
|
||||||
</label>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div className={styles.formButtons}>
|
|
||||||
<FormButton onClick={onSubmit}>Enregistrer</FormButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
@ -17,7 +17,6 @@ import {
|
|||||||
} from "./AskParticipationDetails"
|
} from "./AskParticipationDetails"
|
||||||
|
|
||||||
import { OnSiteInfo, fetchFor as fetchForOnSiteInfo } from "./OnSiteInfo"
|
import { OnSiteInfo, fetchFor as fetchForOnSiteInfo } from "./OnSiteInfo"
|
||||||
// import { AskPushNotif } from "./AskPushNotif"
|
|
||||||
|
|
||||||
const Asks = (): JSX.Element | null => {
|
const Asks = (): JSX.Element | null => {
|
||||||
const { volunteerAsks } = useAskTools()
|
const { volunteerAsks } = useAskTools()
|
||||||
@ -35,8 +34,6 @@ const Asks = (): JSX.Element | null => {
|
|||||||
AskHosting(asks, 20)
|
AskHosting(asks, 20)
|
||||||
AskParticipationDetails(asks, 22)
|
AskParticipationDetails(asks, 22)
|
||||||
|
|
||||||
// AskPushNotif(asks, 99)
|
|
||||||
|
|
||||||
const onSiteInfoElement = OnSiteInfo()
|
const onSiteInfoElement = OnSiteInfo()
|
||||||
if (_.isEmpty(asks)) {
|
if (_.isEmpty(asks)) {
|
||||||
asks.push(onSiteInfoElement)
|
asks.push(onSiteInfoElement)
|
||||||
|
@ -13,14 +13,6 @@
|
|||||||
@include page-content-wrapper(520px);
|
@include page-content-wrapper(520px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pushNotificationsPage {
|
|
||||||
@include page-wrapper-center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pushNotificationsContent {
|
|
||||||
@include page-content-wrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
export default {
|
|
||||||
HOST: "localhost",
|
|
||||||
PORT: 3000,
|
|
||||||
API_URL: "http://localhost:3000",
|
|
||||||
GOOGLE_SHEET_ID: "1p8TDSNlgKC7sm1a_wX44NrkpWEH3-Zey1O2ZjYfPsn4",
|
|
||||||
APP: {
|
|
||||||
htmlAttributes: { lang: "en" },
|
|
||||||
title: "Force Orange",
|
|
||||||
description: "Le site des bénévoles",
|
|
||||||
titleTemplate: "Force Orange - %s",
|
|
||||||
meta: [
|
|
||||||
{
|
|
||||||
name: "description",
|
|
||||||
content: "The best react universal starter boilerplate in the world.",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
DEV_JWT_SECRET: "fakeqA6uF#msq2312bebf2FLFn4XzWQ6dttXSJwBX#?gL2JWf!",
|
|
||||||
DEV_DISCORD_TOKEN: "fakeqA6uF#msq2312bebf2FLFn4XzWQ6dttXSJwBX#?gL2JWf!",
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
import defaultConfig from "./default"
|
|
||||||
import prodConfig from "./prod"
|
|
||||||
|
|
||||||
export default __DEV__ ? defaultConfig : { ...defaultConfig, ...prodConfig }
|
|
@ -1,14 +0,0 @@
|
|||||||
import isNode from "detect-node"
|
|
||||||
|
|
||||||
const PROTOCOL = (typeof window !== "undefined" && window?.location?.protocol) || "http:"
|
|
||||||
const PORT = 4000 + (PROTOCOL === "https:" ? 2 : 0)
|
|
||||||
const API_URL =
|
|
||||||
__DEV__ || __LOCAL__ || isNode
|
|
||||||
? `${PROTOCOL}//localhost:${PORT}`
|
|
||||||
: `${PROTOCOL}//fo.parisestludique.fr`
|
|
||||||
|
|
||||||
export default {
|
|
||||||
PORT,
|
|
||||||
HOST: "0.0.0.0",
|
|
||||||
API_URL,
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
/* Copyright Coplay. All Rights Reserved. Use of this source code is governed by an MIT-style license that can be found in the LICENSE file at https://coplay.org/colicense */
|
|
||||||
import { NextFunction, Request, Response, Router } from "express"
|
|
||||||
import * as path from "path"
|
|
||||||
|
|
||||||
const certbotRouter: Router = Router()
|
|
||||||
|
|
||||||
certbotRouter.use((request: Request, response: Response, _next: NextFunction) => {
|
|
||||||
const filename = request.originalUrl.replace(/.*\//, "")
|
|
||||||
const resolvedPath: string = path.resolve(`../certbot/.well-known/acme-challenge/${filename}`)
|
|
||||||
response.setHeader("Content-Type", "text/html")
|
|
||||||
return response.sendFile(resolvedPath)
|
|
||||||
})
|
|
||||||
|
|
||||||
export default certbotRouter
|
|
@ -23,7 +23,6 @@ import {
|
|||||||
DiscordRoleWithoutId,
|
DiscordRoleWithoutId,
|
||||||
} from "../services/discordRoles"
|
} from "../services/discordRoles"
|
||||||
import { getSheet } from "./gsheets/accessors"
|
import { getSheet } from "./gsheets/accessors"
|
||||||
import config from "../config"
|
|
||||||
|
|
||||||
let cachedToken: string
|
let cachedToken: string
|
||||||
// let cachedClientId: string
|
// let cachedClientId: string
|
||||||
@ -68,7 +67,7 @@ export async function hasDiscordAccess(): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// export async function discordRegisterCommands(): Promise<void> {
|
// export async function discordRegisterCommands(): Promise<void> {
|
||||||
// if (!__REGISTER_DISCORD_COMMANDS__) {
|
// if (!REGISTER_DISCORD_COMMANDS) {
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
@ -98,7 +97,7 @@ export async function hasDiscordAccess(): Promise<boolean> {
|
|||||||
|
|
||||||
export async function discordBot(): Promise<void> {
|
export async function discordBot(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
if (__REGISTER_DISCORD_COMMANDS__) {
|
if (REGISTER_DISCORD_COMMANDS) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,7 +420,7 @@ async function getCreds(): Promise<void> {
|
|||||||
// cachedClientId = parsedCreds.clientId
|
// cachedClientId = parsedCreds.clientId
|
||||||
cachedGuildId = parsedCreds.guildId
|
cachedGuildId = parsedCreds.guildId
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
cachedToken = config.DEV_DISCORD_TOKEN
|
cachedToken = "fakeqA6uF#msq2312bebf2FLFn4XzWQ6dttXSJwBX#?gL2JWf!" // DEV_DISCORD_TOKEN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,11 +50,11 @@ export async function saveLocalDb(
|
|||||||
states[name] = state
|
states[name] = state
|
||||||
types[name] = type
|
types[name] = type
|
||||||
const toSave = { states, types }
|
const toSave = { states, types }
|
||||||
const jsonDB = __DEV__ ? JSON.stringify(toSave, null, 4) : JSON.stringify(toSave)
|
const jsonDB = DEV ? JSON.stringify(toSave, null, 4) : JSON.stringify(toSave)
|
||||||
await fs.writeFile(DB_PATH, jsonDB)
|
await fs.writeFile(DB_PATH, jsonDB)
|
||||||
|
|
||||||
toSave.states = anonimizedDb(toSave.states)
|
toSave.states = anonimizedDb(toSave.states)
|
||||||
const jsonAnonimizedDB = __DEV__ ? JSON.stringify(toSave, null, 4) : JSON.stringify(toSave)
|
const jsonAnonimizedDB = DEV ? JSON.stringify(toSave, null, 4) : JSON.stringify(toSave)
|
||||||
await fs.writeFile(ANONYMIZED_DB_PATH, jsonAnonimizedDB)
|
await fs.writeFile(ANONYMIZED_DB_PATH, jsonAnonimizedDB)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,10 +341,6 @@ function anonimizedNotifs(v: Volunteer): void {
|
|||||||
} else if (v.id % 251 === 0) {
|
} else if (v.id % 251 === 0) {
|
||||||
v.acceptsNotifs = "non"
|
v.acceptsNotifs = "non"
|
||||||
}
|
}
|
||||||
v.pushNotifSubscription =
|
|
||||||
v.id % 13 === 0
|
|
||||||
? '{"endpoint":"https://fcm.googleapis.com/fcm/send/f-EAfakedfakedU:APA91fakedfakedzIk-DEglfakedfaked9ugI--ljtfakedfakedfakedfakedfakedfakedP3t-ggU7Afakedfakedfakedkai","expirationTime":null,"keys":{"p256dh":"BEZOJSfakedfakedfakedfakedfakedfakedfakedfakedfakedfakedgYs-cafakedw","auth":"GlMfakedfakedFRg"}}'
|
|
||||||
: ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function numberToRand(n: number) {
|
function numberToRand(n: number) {
|
||||||
|
@ -69,7 +69,7 @@ async function sendMeetingEmail(
|
|||||||
const hasMeetingDates = miscList?.[0]?.meetingId !== ""
|
const hasMeetingDates = miscList?.[0]?.meetingId !== ""
|
||||||
|
|
||||||
if (!hasMeetingDates || firstMeeting === "") {
|
if (!hasMeetingDates || firstMeeting === "") {
|
||||||
if (__DEV__ || apiKey === "") {
|
if (DEV || apiKey === "") {
|
||||||
console.error(`Fake sending meeting email to ${email}`)
|
console.error(`Fake sending meeting email to ${email}`)
|
||||||
} else {
|
} else {
|
||||||
sgMail.setApiKey(apiKey)
|
sgMail.setApiKey(apiKey)
|
||||||
@ -93,7 +93,7 @@ async function sendMeetingEmail(
|
|||||||
}
|
}
|
||||||
const { meetingTitle, meetingUrl } = meetingLine
|
const { meetingTitle, meetingUrl } = meetingLine
|
||||||
|
|
||||||
if (__DEV__ || apiKey === "") {
|
if (DEV || apiKey === "") {
|
||||||
console.error(
|
console.error(
|
||||||
`Fake sending meeting email to ${email} for ${meetingTitle} and url ${meetingUrl}`
|
`Fake sending meeting email to ${email} for ${meetingTitle} and url ${meetingUrl}`
|
||||||
)
|
)
|
||||||
|
@ -136,8 +136,8 @@ export const volunteerPartialAdd = expressAccessor.add(async (list, body) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
async function sendSignUpEmail(email: string, password: string): Promise<void> {
|
async function sendSignUpEmail(email: string, password: string): Promise<void> {
|
||||||
const apiKey = process.env.SENDGRID_API_KEY || ""
|
const apiKey = process.env.SENDGRID_API_KEY || null
|
||||||
if (__DEV__ || apiKey === "") {
|
if (DEV || !apiKey) {
|
||||||
console.error(`Fake sending signup email to ${email} with password ${password}`)
|
console.error(`Fake sending signup email to ${email} with password ${password}`)
|
||||||
} else {
|
} else {
|
||||||
sgMail.setApiKey(apiKey)
|
sgMail.setApiKey(apiKey)
|
||||||
@ -178,7 +178,7 @@ export const volunteerLogin = expressAccessor.get<VolunteerLogin>(async (list, b
|
|||||||
)
|
)
|
||||||
|
|
||||||
const noSuccessfulLogin = !some(tries)
|
const noSuccessfulLogin = !some(tries)
|
||||||
const isDevException = __DEV__ && [1, 508].includes(volunteer.id) // Amélie and Tom E
|
const isDevException = DEV && [1, 508].includes(volunteer.id) // Amélie and Tom E
|
||||||
if (noSuccessfulLogin && !isDevException) {
|
if (noSuccessfulLogin && !isDevException) {
|
||||||
throw Error("Mauvais mot de passe pour cet email")
|
throw Error("Mauvais mot de passe pour cet email")
|
||||||
}
|
}
|
||||||
@ -234,8 +234,8 @@ function generatePassword(): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function sendForgetEmail(email: string, password: string): Promise<void> {
|
async function sendForgetEmail(email: string, password: string): Promise<void> {
|
||||||
const apiKey = process.env.SENDGRID_API_KEY || ""
|
const apiKey = process.env.SENDGRID_API_KEY || null
|
||||||
if (__DEV__ || apiKey === "") {
|
if (DEV || !apiKey) {
|
||||||
console.error(`Fake sending forget email to ${email} with password ${password}`)
|
console.error(`Fake sending forget email to ${email} with password ${password}`)
|
||||||
} else {
|
} else {
|
||||||
sgMail.setApiKey(apiKey)
|
sgMail.setApiKey(apiKey)
|
||||||
@ -265,8 +265,6 @@ export const volunteerAsksSet = expressAccessor.set(async (list, body, id) => {
|
|||||||
if (notifChanges.hiddenAsks !== undefined) newVolunteer.hiddenAsks = notifChanges.hiddenAsks
|
if (notifChanges.hiddenAsks !== undefined) newVolunteer.hiddenAsks = notifChanges.hiddenAsks
|
||||||
if (notifChanges.acceptsNotifs !== undefined)
|
if (notifChanges.acceptsNotifs !== undefined)
|
||||||
newVolunteer.acceptsNotifs = notifChanges.acceptsNotifs
|
newVolunteer.acceptsNotifs = notifChanges.acceptsNotifs
|
||||||
if (notifChanges.pushNotifSubscription !== undefined)
|
|
||||||
newVolunteer.pushNotifSubscription = notifChanges.pushNotifSubscription
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
toDatabase: newVolunteer,
|
toDatabase: newVolunteer,
|
||||||
@ -274,7 +272,6 @@ export const volunteerAsksSet = expressAccessor.set(async (list, body, id) => {
|
|||||||
id: newVolunteer.id,
|
id: newVolunteer.id,
|
||||||
firstname: newVolunteer.firstname,
|
firstname: newVolunteer.firstname,
|
||||||
hiddenAsks: newVolunteer.hiddenAsks,
|
hiddenAsks: newVolunteer.hiddenAsks,
|
||||||
pushNotifSubscription: newVolunteer.pushNotifSubscription,
|
|
||||||
acceptsNotifs: newVolunteer.acceptsNotifs,
|
acceptsNotifs: newVolunteer.acceptsNotifs,
|
||||||
} as VolunteerAsks,
|
} as VolunteerAsks,
|
||||||
}
|
}
|
||||||
@ -483,6 +480,7 @@ function setNewPhoto(id: number, photoData: string, prevFilename: string | undef
|
|||||||
const buffer = Buffer.from(base64Data, "base64")
|
const buffer = Buffer.from(base64Data, "base64")
|
||||||
const filename = `${id}.${ext}`
|
const filename = `${id}.${ext}`
|
||||||
const filePath = path.resolve(process.cwd(), `public/photos/${filename}`)
|
const filePath = path.resolve(process.cwd(), `public/photos/${filename}`)
|
||||||
|
// TODO move picture in cloud storage
|
||||||
if (prevFilename) {
|
if (prevFilename) {
|
||||||
const prevFilePath = path.resolve(process.cwd(), `public/photos/${prevFilename}`)
|
const prevFilePath = path.resolve(process.cwd(), `public/photos/${prevFilename}`)
|
||||||
fs.unlinkSync(prevFilePath)
|
fs.unlinkSync(prevFilePath)
|
||||||
|
@ -8,14 +8,10 @@ import hpp from "hpp"
|
|||||||
import favicon from "serve-favicon"
|
import favicon from "serve-favicon"
|
||||||
import chalk from "chalk"
|
import chalk from "chalk"
|
||||||
import * as http from "http"
|
import * as http from "http"
|
||||||
import * as https from "https"
|
|
||||||
import * as fs from "fs"
|
|
||||||
import _ from "lodash"
|
|
||||||
|
|
||||||
import devServer from "./devServer"
|
import devServer from "./devServer"
|
||||||
import ssr from "./ssr"
|
import ssr from "./ssr"
|
||||||
|
|
||||||
import certbotRouter from "../routes/certbot"
|
|
||||||
import { hasSecret, secure } from "./secure"
|
import { hasSecret, secure } from "./secure"
|
||||||
import { announcementListGet } from "./gsheets/announcements"
|
import { announcementListGet } from "./gsheets/announcements"
|
||||||
import { detailedBoxListGet } from "./gsheets/boxes"
|
import { detailedBoxListGet } from "./gsheets/boxes"
|
||||||
@ -49,12 +45,10 @@ import {
|
|||||||
volunteerOnSiteInfo,
|
volunteerOnSiteInfo,
|
||||||
} from "./gsheets/volunteers"
|
} from "./gsheets/volunteers"
|
||||||
import { wishListGet, wishAdd } from "./gsheets/wishes"
|
import { wishListGet, wishAdd } from "./gsheets/wishes"
|
||||||
import config from "../config"
|
|
||||||
import { notificationsSubscribe, notificationMain } from "./notifications"
|
|
||||||
import { /* discordRegisterCommands, */ discordBot, hasDiscordAccess } from "./discordBot"
|
import { /* discordRegisterCommands, */ discordBot, hasDiscordAccess } from "./discordBot"
|
||||||
import checkAccess from "./checkAccess"
|
import checkAccess from "./checkAccess"
|
||||||
import { hasGSheetsAccess } from "./gsheets/accessors"
|
import { hasGSheetsAccess } from "./gsheets/accessors"
|
||||||
import { addStatus, showStatusAt } from "./status"
|
import { addStatus } from "./status"
|
||||||
import {
|
import {
|
||||||
miscDiscordInvitation,
|
miscDiscordInvitation,
|
||||||
miscFestivalDateListGet,
|
miscFestivalDateListGet,
|
||||||
@ -64,11 +58,11 @@ import { retexSet } from "./gsheets/retex"
|
|||||||
|
|
||||||
checkAccess()
|
checkAccess()
|
||||||
|
|
||||||
notificationMain()
|
|
||||||
|
|
||||||
// discordRegisterCommands()
|
// discordRegisterCommands()
|
||||||
discordBot()
|
discordBot()
|
||||||
|
|
||||||
|
const { PORT } = process.env
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
|
|
||||||
// Allow receiving big images
|
// Allow receiving big images
|
||||||
@ -81,10 +75,6 @@ app.use(helmet({ contentSecurityPolicy: false }))
|
|||||||
app.use(hpp())
|
app.use(hpp())
|
||||||
// Compress all requests
|
// Compress all requests
|
||||||
app.use(compression())
|
app.use(compression())
|
||||||
// Https with certbot and Let's Encrypt
|
|
||||||
if (!__DEV__) {
|
|
||||||
app.use("/.well-known/acme-challenge", certbotRouter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use for http request debug (show errors only)
|
// Use for http request debug (show errors only)
|
||||||
app.use(logger("dev", { skip: (_req, res) => res.statusCode < 400 }))
|
app.use(logger("dev", { skip: (_req, res) => res.statusCode < 400 }))
|
||||||
@ -92,7 +82,7 @@ app.use(favicon(path.resolve(process.cwd(), "public/favicon.ico")))
|
|||||||
app.use(express.static(path.resolve(process.cwd(), "public")))
|
app.use(express.static(path.resolve(process.cwd(), "public")))
|
||||||
|
|
||||||
// Enable dev-server in development
|
// Enable dev-server in development
|
||||||
if (__DEV__) devServer(app)
|
if (DEV) devServer(app)
|
||||||
|
|
||||||
app.use(express.json())
|
app.use(express.json())
|
||||||
app.use(cookieParser())
|
app.use(cookieParser())
|
||||||
@ -106,126 +96,81 @@ app.get(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const apiRouter = express.Router()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* APIs
|
* APIs
|
||||||
*/
|
*/
|
||||||
// Google Sheets API
|
// Google Sheets API
|
||||||
app.get("/GameDetailsUpdate", gameDetailsUpdate)
|
apiRouter.get("/GameDetailsUpdate", gameDetailsUpdate)
|
||||||
app.get("/BoxDetailedListGet", detailedBoxListGet)
|
apiRouter.get("/BoxDetailedListGet", detailedBoxListGet)
|
||||||
app.get("/GameListGet", gameListGet)
|
apiRouter.get("/GameListGet", gameListGet)
|
||||||
app.get("/GamesToGiveListGet", gamesToGiveListGet)
|
apiRouter.get("/GamesToGiveListGet", gamesToGiveListGet)
|
||||||
app.get("/GameTitleOrderCategories", gameTitleOrderCategories)
|
apiRouter.get("/GameTitleOrderCategories", gameTitleOrderCategories)
|
||||||
app.get("/MiscFestivalDateListGet", miscFestivalDateListGet)
|
apiRouter.get("/MiscFestivalDateListGet", miscFestivalDateListGet)
|
||||||
app.get("/MiscMeetingDateListGet", miscMeetingDateListGet)
|
apiRouter.get("/MiscMeetingDateListGet", miscMeetingDateListGet)
|
||||||
app.get("/WishListGet", wishListGet)
|
apiRouter.get("/WishListGet", wishListGet)
|
||||||
app.post("/WishAdd", wishAdd)
|
apiRouter.post("/WishAdd", wishAdd)
|
||||||
app.post("/PostulantAdd", postulantAdd)
|
apiRouter.post("/PostulantAdd", postulantAdd)
|
||||||
// Disabling registration app.post("/VolunteerPartialAdd", volunteerPartialAdd)
|
// Disabling registration apiRouter.post("/VolunteerPartialAdd", volunteerPartialAdd)
|
||||||
app.post("/VolunteerLogin", volunteerLogin)
|
apiRouter.post("/VolunteerLogin", volunteerLogin)
|
||||||
app.post("/VolunteerForgot", volunteerForgot)
|
apiRouter.post("/VolunteerForgot", volunteerForgot)
|
||||||
app.get("/VolunteerListGet", secure as RequestHandler, volunteerListGet)
|
apiRouter.get("/VolunteerListGet", secure as RequestHandler, volunteerListGet)
|
||||||
|
|
||||||
// Secured APIs
|
// Secured APIs
|
||||||
app.get("/AnnouncementListGet", secure as RequestHandler, announcementListGet)
|
apiRouter.get("/AnnouncementListGet", secure as RequestHandler, announcementListGet)
|
||||||
app.get("/GameWithVolunteersListGet", secure as RequestHandler, gameWithVolunteersListGet)
|
apiRouter.get("/GameWithVolunteersListGet", secure as RequestHandler, gameWithVolunteersListGet)
|
||||||
app.get("/MiscDiscordInvitationGet", secure as RequestHandler, miscDiscordInvitation)
|
apiRouter.get("/MiscDiscordInvitationGet", secure as RequestHandler, miscDiscordInvitation)
|
||||||
app.post("/RetexSet", secure as RequestHandler, retexSet)
|
apiRouter.post("/RetexSet", secure as RequestHandler, retexSet)
|
||||||
app.get("/TeamListGet", teamListGet)
|
apiRouter.get("/TeamListGet", teamListGet)
|
||||||
app.get("/VolunteerDiscordId", secure as RequestHandler, volunteerDiscordId)
|
apiRouter.get("/VolunteerDiscordId", secure as RequestHandler, volunteerDiscordId)
|
||||||
app.post("/VolunteerAsksSet", secure as RequestHandler, volunteerAsksSet)
|
apiRouter.post("/VolunteerAsksSet", secure as RequestHandler, volunteerAsksSet)
|
||||||
app.post("/VolunteerKnowledgeSet", secure as RequestHandler, volunteerKnowledgeSet)
|
apiRouter.post("/VolunteerKnowledgeSet", secure as RequestHandler, volunteerKnowledgeSet)
|
||||||
app.post(
|
apiRouter.post(
|
||||||
"/VolunteerDetailedKnowledgeListGet",
|
"/VolunteerDetailedKnowledgeListGet",
|
||||||
secure as RequestHandler,
|
secure as RequestHandler,
|
||||||
volunteerDetailedKnowledgeList
|
volunteerDetailedKnowledgeList
|
||||||
)
|
)
|
||||||
app.post("/VolunteerLoanSet", secure as RequestHandler, volunteerLoanSet)
|
apiRouter.post("/VolunteerLoanSet", secure as RequestHandler, volunteerLoanSet)
|
||||||
app.post(
|
apiRouter.post(
|
||||||
"/VolunteerParticipationDetailsSet",
|
"/VolunteerParticipationDetailsSet",
|
||||||
secure as RequestHandler,
|
secure as RequestHandler,
|
||||||
volunteerParticipationDetailsSet
|
volunteerParticipationDetailsSet
|
||||||
)
|
)
|
||||||
app.post("/VolunteerDayWishesSet", secure as RequestHandler, volunteerDayWishesSet)
|
apiRouter.post("/VolunteerDayWishesSet", secure as RequestHandler, volunteerDayWishesSet)
|
||||||
app.post("/VolunteerHostingSet", secure as RequestHandler, volunteerHostingSet)
|
apiRouter.post("/VolunteerHostingSet", secure as RequestHandler, volunteerHostingSet)
|
||||||
app.post("/VolunteerMealsSet", secure as RequestHandler, volunteerMealsSet)
|
apiRouter.post("/VolunteerMealsSet", secure as RequestHandler, volunteerMealsSet)
|
||||||
app.post("/VolunteerPersonalInfoSet", secure as RequestHandler, volunteerPersonalInfoSet)
|
apiRouter.post("/VolunteerPersonalInfoSet", secure as RequestHandler, volunteerPersonalInfoSet)
|
||||||
app.post("/VolunteerTeamWishesSet", secure as RequestHandler, volunteerTeamWishesSet)
|
apiRouter.post("/VolunteerTeamWishesSet", secure as RequestHandler, volunteerTeamWishesSet)
|
||||||
app.post("/VolunteerTeamAssignSet", secure as RequestHandler, volunteerTeamAssignSet)
|
apiRouter.post("/VolunteerTeamAssignSet", secure as RequestHandler, volunteerTeamAssignSet)
|
||||||
app.get("/VolunteerOnSiteInfo", secure as RequestHandler, volunteerOnSiteInfo)
|
apiRouter.get("/VolunteerOnSiteInfo", secure as RequestHandler, volunteerOnSiteInfo)
|
||||||
|
|
||||||
// Admin only
|
// Admin only
|
||||||
app.post("/VolunteerAddNew", secure as RequestHandler, volunteerAddNew)
|
apiRouter.post("/VolunteerAddNew", secure as RequestHandler, volunteerAddNew)
|
||||||
app.post("/VolunteerSet", secure as RequestHandler, volunteerSet)
|
apiRouter.post("/VolunteerSet", secure as RequestHandler, volunteerSet)
|
||||||
|
|
||||||
// Push notification subscription
|
app.use("/api", apiRouter)
|
||||||
app.post("/notifications/subscribe", notificationsSubscribe)
|
|
||||||
|
|
||||||
// Use React server-side rendering middleware
|
// Use React server-side rendering middleware
|
||||||
app.get("*", ssr)
|
app.get("*", ssr)
|
||||||
|
|
||||||
/**
|
|
||||||
* Create HTTP and HTTPS server.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const servers = [{ protocol: "http", server: http.createServer(app) }]
|
|
||||||
|
|
||||||
interface Cert {
|
|
||||||
key: string
|
|
||||||
cert: string
|
|
||||||
}
|
|
||||||
const certPaths: Cert[] = [
|
|
||||||
{
|
|
||||||
// Prod
|
|
||||||
key: "/root/certbot/config/live/fo.parisestludique.fr/privkey.pem",
|
|
||||||
cert: "/root/certbot/config/live/fo.parisestludique.fr/fullchain.pem",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Local
|
|
||||||
key: "../certbot/key.pem",
|
|
||||||
cert: "../certbot/cert.pem",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
const validCertPath: Cert | undefined = certPaths.find((certPath: Cert) =>
|
|
||||||
_.every(certPath, (pemPath: string) => fs.existsSync(pemPath))
|
|
||||||
)
|
|
||||||
if (validCertPath) {
|
|
||||||
const httpsOptions = _.mapValues(validCertPath, (pemPath: string) => fs.readFileSync(pemPath))
|
|
||||||
|
|
||||||
servers.push({ protocol: "https", server: https.createServer(httpsOptions, app) })
|
|
||||||
|
|
||||||
showStatusAt(6)
|
|
||||||
} else {
|
|
||||||
showStatusAt(5)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listen on provided port, on all network interfaces.
|
* Listen on provided port, on all network interfaces.
|
||||||
*/
|
*/
|
||||||
servers.forEach(({ protocol, server }) => {
|
|
||||||
server.listen(protocol === "http" ? config.PORT : <number>config.PORT + 2)
|
|
||||||
server.on("error", onError)
|
|
||||||
server.on("listening", () => onListening(server))
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
const server = http.createServer(app)
|
||||||
* Event listener for HTTP server 'error' event.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function onError(error: any) {
|
server.listen(PORT)
|
||||||
|
server.on("error", (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
addStatus("Server listening:", chalk.red(`==> 😭 OMG!!! ${error}`))
|
addStatus("Server listening:", chalk.red(`==> 😭 OMG!!! ${error}`))
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
server.on("listening", () => {
|
||||||
/**
|
|
||||||
* Event listener for HTTP server 'listening' event.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function onListening(server: any) {
|
|
||||||
const addr = server.address()
|
const addr = server.address()
|
||||||
const bind = typeof addr === "string" ? `pipe ${addr}` : `port ${addr.port}`
|
const bind = typeof addr === "string" ? `pipe ${addr}` : `port ${addr?.port}`
|
||||||
addStatus("Server listening:", chalk.green(`✅ ${bind}`))
|
addStatus("Server listening:", chalk.green(`✅ ${bind}`))
|
||||||
}
|
})
|
||||||
|
|
||||||
hasGSheetsAccess().then((hasApiAccess: boolean) => {
|
hasGSheetsAccess().then((hasApiAccess: boolean) => {
|
||||||
if (hasApiAccess) {
|
if (hasApiAccess) {
|
||||||
@ -242,13 +187,6 @@ if (hasSendGridApiAccess) {
|
|||||||
addStatus("Emailing:", chalk.blue(`🚧 offline, simulated`))
|
addStatus("Emailing:", chalk.blue(`🚧 offline, simulated`))
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasPushNotifAccess = !!process.env.FORCE_ORANGE_PUBLIC_VAPID_KEY
|
|
||||||
if (hasPushNotifAccess) {
|
|
||||||
addStatus("Push notif:", chalk.green(`✅ online with a Vapid key`))
|
|
||||||
} else {
|
|
||||||
addStatus("Push notif:", chalk.blue(`🚧 offline, simulated`))
|
|
||||||
}
|
|
||||||
|
|
||||||
hasDiscordAccess().then((hasApiAccess: boolean) => {
|
hasDiscordAccess().then((hasApiAccess: boolean) => {
|
||||||
if (hasApiAccess) {
|
if (hasApiAccess) {
|
||||||
addStatus("Discord bot:", chalk.green(`✅ online through discord.js`))
|
addStatus("Discord bot:", chalk.green(`✅ online through discord.js`))
|
||||||
|
@ -1,138 +0,0 @@
|
|||||||
import { lowerFirst } from "lodash"
|
|
||||||
import { Request, Response, NextFunction } from "express"
|
|
||||||
import webpush from "web-push"
|
|
||||||
import {
|
|
||||||
Announcement,
|
|
||||||
AnnouncementWithoutId,
|
|
||||||
translationAnnouncement,
|
|
||||||
} from "../services/announcement"
|
|
||||||
import { translationVolunteer, Volunteer, VolunteerWithoutId } from "../services/volunteers"
|
|
||||||
import { getSheet } from "./gsheets/accessors"
|
|
||||||
|
|
||||||
const publicKey = process.env.FORCE_ORANGE_PUBLIC_VAPID_KEY
|
|
||||||
const privateKey = process.env.FORCE_ORANGE_PRIVATE_VAPID_KEY
|
|
||||||
const hasPushAccess = publicKey && privateKey && !__DEV__ && !__LOCAL__
|
|
||||||
if (hasPushAccess) {
|
|
||||||
webpush.setVapidDetails("mailto: contact@parisestludique.fr", publicKey, privateKey)
|
|
||||||
}
|
|
||||||
type Payload = {
|
|
||||||
message: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function sendNotifToVolunteer(volunteer: Volunteer, payload: Payload): void {
|
|
||||||
if (volunteer.acceptsNotifs !== "oui") {
|
|
||||||
console.error(`Volunteer refuses notifs`)
|
|
||||||
} else {
|
|
||||||
const subscription = JSON.parse(volunteer.pushNotifSubscription)
|
|
||||||
|
|
||||||
if (!subscription) {
|
|
||||||
console.error(`Volunteer has no notif subscription`)
|
|
||||||
} else if (hasPushAccess) {
|
|
||||||
const stringifiedPayload = JSON.stringify(payload)
|
|
||||||
webpush
|
|
||||||
.sendNotification(subscription, stringifiedPayload)
|
|
||||||
.then((result) => console.error(result))
|
|
||||||
.catch((e) => console.error(e.stack))
|
|
||||||
} else {
|
|
||||||
console.error(
|
|
||||||
`Fake sending push notif to ${JSON.stringify(subscription)} of ${JSON.stringify(
|
|
||||||
payload
|
|
||||||
)})}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function sendNotifToSubscription(
|
|
||||||
subscription: webpush.PushSubscription,
|
|
||||||
payload: Payload
|
|
||||||
): void {
|
|
||||||
if (hasPushAccess) {
|
|
||||||
const stringifiedPayload = JSON.stringify(payload)
|
|
||||||
webpush
|
|
||||||
.sendNotification(subscription, stringifiedPayload)
|
|
||||||
.then((result) => console.error(result))
|
|
||||||
.catch((e) => console.error(e.stack))
|
|
||||||
} else {
|
|
||||||
console.error(
|
|
||||||
`Fake sending push notif to ${JSON.stringify(subscription)} of ${JSON.stringify(
|
|
||||||
payload
|
|
||||||
)})}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function notificationsSubscribe(
|
|
||||||
request: Request,
|
|
||||||
response: Response,
|
|
||||||
_next: NextFunction
|
|
||||||
): void {
|
|
||||||
sendNotifToSubscription(request.body, {
|
|
||||||
message:
|
|
||||||
"Clique-moi pour voir la gazette de février dans la page Annonces !\nFini les emails, cette notification sera notre seul moyen de te prévenir :)",
|
|
||||||
})
|
|
||||||
|
|
||||||
response.status(200).json({ success: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
export function notificationMain(): void {
|
|
||||||
setInterval(notifyAboutAnnouncement, 5 * 60 * 1000)
|
|
||||||
setTimeout(notifyAboutAnnouncement, 60 * 1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function notifyAboutAnnouncement(): Promise<void> {
|
|
||||||
const announcementSheet = await getSheet<AnnouncementWithoutId, Announcement>(
|
|
||||||
"Announcements",
|
|
||||||
new Announcement(),
|
|
||||||
translationAnnouncement
|
|
||||||
)
|
|
||||||
|
|
||||||
const announcementList = await announcementSheet.getList()
|
|
||||||
if (!announcementList) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const toSend = announcementList.find((a) => !a.informedWithNotif)
|
|
||||||
if (!toSend) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const volunteerSheet = await getSheet<VolunteerWithoutId, Volunteer>(
|
|
||||||
"Volunteers",
|
|
||||||
new Volunteer(),
|
|
||||||
translationVolunteer
|
|
||||||
)
|
|
||||||
const volunteerList = await volunteerSheet.getList()
|
|
||||||
if (!volunteerList) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const audience = volunteerList.filter(
|
|
||||||
(v) =>
|
|
||||||
v.acceptsNotifs === "oui" &&
|
|
||||||
(v.active === "oui" || v.active === "peut-etre" || v.active === "à distance")
|
|
||||||
)
|
|
||||||
|
|
||||||
console.error(
|
|
||||||
`Sending announcement ${toSend.title} to ${audience
|
|
||||||
.map((v) => `${v.firstname} #${v.id}`)
|
|
||||||
.join(", ")}`
|
|
||||||
)
|
|
||||||
|
|
||||||
const announceDescription: string =
|
|
||||||
{
|
|
||||||
gazette: / - /.test(toSend.title)
|
|
||||||
? `la ${lowerFirst(toSend.title.replace(/.* - /, ""))}`
|
|
||||||
: `la gazette ${lowerFirst(toSend.title)}`,
|
|
||||||
"compte rendu": `le compte-rendu du ${lowerFirst(toSend.title)}`,
|
|
||||||
}[toSend.type] || toSend.title
|
|
||||||
|
|
||||||
audience.forEach((v) => {
|
|
||||||
sendNotifToVolunteer(v, {
|
|
||||||
message: `Clique-moi pour voir ${announceDescription}`,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
toSend.informedWithNotif = true
|
|
||||||
await announcementSheet.set(toSend)
|
|
||||||
}
|
|
@ -31,37 +31,6 @@ export default (
|
|||||||
${extractor.getStyleTags()}
|
${extractor.getStyleTags()}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script>
|
|
||||||
window.isSubscribed = false;
|
|
||||||
window.swRegistration = null;
|
|
||||||
|
|
||||||
//REGISTER AND UNREGISTER SERVICE WORKER
|
|
||||||
if ('serviceWorker' in navigator && 'PushManager' in window) {
|
|
||||||
window.addEventListener('load', function() {
|
|
||||||
var swPath = '/service-worker.js';
|
|
||||||
navigator.serviceWorker.register(swPath)
|
|
||||||
.then(function(registration) {
|
|
||||||
console.log('Service Worker registered');
|
|
||||||
window.swRegistration = registration;
|
|
||||||
registration.pushManager.getSubscription()
|
|
||||||
.then(function(subscription) {
|
|
||||||
console.log("subscription", JSON.stringify(subscription));
|
|
||||||
window.isSubscribed = !(subscription === null);
|
|
||||||
|
|
||||||
if (window.isSubscribed) {
|
|
||||||
console.log('User IS subscribed.');
|
|
||||||
} else {
|
|
||||||
console.log('User is NOT subscribed.');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(function(err) {
|
|
||||||
console.log('Service Worker registration failed: ', err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Insert the router, which passed from server-side -->
|
<!-- Insert the router, which passed from server-side -->
|
||||||
<div id="react-view">${htmlContent}</div>
|
<div id="react-view">${htmlContent}</div>
|
||||||
|
|
||||||
@ -90,5 +59,5 @@ export default (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Minify HTML in production
|
// Minify HTML in production
|
||||||
return __DEV__ ? html : minify(html, minifyConfig)
|
return DEV ? html : minify(html, minifyConfig)
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,6 @@ import path from "path"
|
|||||||
import { constants, promises as fs } from "fs"
|
import { constants, promises as fs } from "fs"
|
||||||
import { verify, sign } from "jsonwebtoken"
|
import { verify, sign } from "jsonwebtoken"
|
||||||
|
|
||||||
import config from "../config"
|
|
||||||
|
|
||||||
type AuthorizedRequest = Request & { headers: { authorization: string } }
|
type AuthorizedRequest = Request & { headers: { authorization: string } }
|
||||||
|
|
||||||
let cachedSecret: string
|
let cachedSecret: string
|
||||||
@ -57,7 +55,7 @@ async function getSecret() {
|
|||||||
const secretContent = await fs.readFile(SECRET_PATH)
|
const secretContent = await fs.readFile(SECRET_PATH)
|
||||||
cachedSecret = secretContent && JSON.parse(secretContent.toString()).secret
|
cachedSecret = secretContent && JSON.parse(secretContent.toString()).secret
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
cachedSecret = config.DEV_JWT_SECRET
|
cachedSecret = "fakeqA6uF#msq2312bebf2FLFn4XzWQ6dttXSJwBX#?gL2JWf!" // DEV_JWT_SECRET
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
import _ from "lodash"
|
import _ from "lodash"
|
||||||
|
|
||||||
import config from "../config"
|
|
||||||
import { axiosConfig } from "./auth"
|
import { axiosConfig } from "./auth"
|
||||||
|
|
||||||
export type ElementWithId = unknown & { id: number }
|
export type ElementWithId = unknown & { id: number }
|
||||||
@ -26,7 +24,7 @@ export default class ServiceAccessors<
|
|||||||
}
|
}
|
||||||
return async (id: number): Promise<ElementGetResponse> => {
|
return async (id: number): Promise<ElementGetResponse> => {
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.get(`${config.API_URL}/${this.elementName}Get`, {
|
const { data } = await axios.get(`${API_URL}/${this.elementName}Get`, {
|
||||||
...axiosConfig,
|
...axiosConfig,
|
||||||
params: { id },
|
params: { id },
|
||||||
})
|
})
|
||||||
@ -51,7 +49,7 @@ export default class ServiceAccessors<
|
|||||||
return async (): Promise<ElementListGetResponse> => {
|
return async (): Promise<ElementListGetResponse> => {
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.get(
|
const { data } = await axios.get(
|
||||||
`${config.API_URL}/${this.elementName}ListGet`,
|
`${API_URL}/${this.elementName}ListGet`,
|
||||||
axiosConfig
|
axiosConfig
|
||||||
)
|
)
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
@ -77,7 +75,7 @@ export default class ServiceAccessors<
|
|||||||
const auth = { headers: { Authorization: `Bearer ${jwt}` } }
|
const auth = { headers: { Authorization: `Bearer ${jwt}` } }
|
||||||
const fullAxiosConfig = _.defaultsDeep(auth, axiosConfig)
|
const fullAxiosConfig = _.defaultsDeep(auth, axiosConfig)
|
||||||
const { data } = await axios.get(
|
const { data } = await axios.get(
|
||||||
`${config.API_URL}/${this.elementName}ListGet`,
|
`${API_URL}/${this.elementName}ListGet`,
|
||||||
fullAxiosConfig
|
fullAxiosConfig
|
||||||
)
|
)
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
@ -107,7 +105,7 @@ export default class ServiceAccessors<
|
|||||||
try {
|
try {
|
||||||
const auth = { headers: { Authorization: `Bearer ${jwt}` } }
|
const auth = { headers: { Authorization: `Bearer ${jwt}` } }
|
||||||
const fullAxiosConfig = _.defaultsDeep(auth, axiosConfig)
|
const fullAxiosConfig = _.defaultsDeep(auth, axiosConfig)
|
||||||
const rawData = await axios.get(`${config.API_URL}/${this.elementName}${apiName}`, {
|
const rawData = await axios.get(`${API_URL}/${this.elementName}${apiName}`, {
|
||||||
...fullAxiosConfig,
|
...fullAxiosConfig,
|
||||||
params,
|
params,
|
||||||
})
|
})
|
||||||
@ -135,7 +133,7 @@ export default class ServiceAccessors<
|
|||||||
return async (volunteerWithoutId: ElementNoId): Promise<ElementGetResponse> => {
|
return async (volunteerWithoutId: ElementNoId): Promise<ElementGetResponse> => {
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.post(
|
const { data } = await axios.post(
|
||||||
`${config.API_URL}/${this.elementName}Add`,
|
`${API_URL}/${this.elementName}Add`,
|
||||||
volunteerWithoutId,
|
volunteerWithoutId,
|
||||||
axiosConfig
|
axiosConfig
|
||||||
)
|
)
|
||||||
@ -160,10 +158,11 @@ export default class ServiceAccessors<
|
|||||||
return async (volunteer: Element): Promise<ElementGetResponse> => {
|
return async (volunteer: Element): Promise<ElementGetResponse> => {
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.post(
|
const { data } = await axios.post(
|
||||||
`${config.API_URL}/${this.elementName}Set`,
|
`${API_URL}/${this.elementName}Set`,
|
||||||
volunteer,
|
volunteer,
|
||||||
axiosConfig
|
axiosConfig
|
||||||
)
|
)
|
||||||
|
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
throw Error(data.error)
|
throw Error(data.error)
|
||||||
}
|
}
|
||||||
@ -185,7 +184,7 @@ export default class ServiceAccessors<
|
|||||||
return async (): Promise<ElementCountGetResponse> => {
|
return async (): Promise<ElementCountGetResponse> => {
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.get(
|
const { data } = await axios.get(
|
||||||
`${config.API_URL}/${this.elementName}CountGet`,
|
`${API_URL}/${this.elementName}CountGet`,
|
||||||
axiosConfig
|
axiosConfig
|
||||||
)
|
)
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
@ -210,10 +209,10 @@ export default class ServiceAccessors<
|
|||||||
}
|
}
|
||||||
return async (...params: InputElements): Promise<ElementGetResponse> => {
|
return async (...params: InputElements): Promise<ElementGetResponse> => {
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.get(
|
const { data } = await axios.get(`${API_URL}/${this.elementName}${apiName}`, {
|
||||||
`${config.API_URL}/${this.elementName}${apiName}`,
|
...axiosConfig,
|
||||||
{ ...axiosConfig, params }
|
params,
|
||||||
)
|
})
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
throw Error(data.error)
|
throw Error(data.error)
|
||||||
}
|
}
|
||||||
@ -237,7 +236,7 @@ export default class ServiceAccessors<
|
|||||||
return async (...params: InputElements): Promise<ElementGetResponse> => {
|
return async (...params: InputElements): Promise<ElementGetResponse> => {
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.post(
|
const { data } = await axios.post(
|
||||||
`${config.API_URL}/${this.elementName}${apiName}`,
|
`${API_URL}/${this.elementName}${apiName}`,
|
||||||
params,
|
params,
|
||||||
axiosConfig
|
axiosConfig
|
||||||
)
|
)
|
||||||
@ -268,10 +267,10 @@ export default class ServiceAccessors<
|
|||||||
try {
|
try {
|
||||||
const auth = { headers: { Authorization: `Bearer ${jwt}` } }
|
const auth = { headers: { Authorization: `Bearer ${jwt}` } }
|
||||||
const fullAxiosConfig = _.defaultsDeep(auth, axiosConfig)
|
const fullAxiosConfig = _.defaultsDeep(auth, axiosConfig)
|
||||||
const { data } = await axios.get(
|
const { data } = await axios.get(`${API_URL}/${this.elementName}${apiName}`, {
|
||||||
`${config.API_URL}/${this.elementName}${apiName}`,
|
...fullAxiosConfig,
|
||||||
{ ...fullAxiosConfig, params }
|
params,
|
||||||
)
|
})
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
throw Error(data.error)
|
throw Error(data.error)
|
||||||
}
|
}
|
||||||
@ -300,7 +299,7 @@ export default class ServiceAccessors<
|
|||||||
const auth = { headers: { Authorization: `Bearer ${jwt}` } }
|
const auth = { headers: { Authorization: `Bearer ${jwt}` } }
|
||||||
const fullAxiosConfig = _.defaultsDeep(auth, axiosConfig)
|
const fullAxiosConfig = _.defaultsDeep(auth, axiosConfig)
|
||||||
const { data } = await axios.post(
|
const { data } = await axios.post(
|
||||||
`${config.API_URL}/${this.elementName}${apiName}`,
|
`${API_URL}/${this.elementName}${apiName}`,
|
||||||
params,
|
params,
|
||||||
fullAxiosConfig
|
fullAxiosConfig
|
||||||
)
|
)
|
||||||
|
@ -48,8 +48,6 @@ export class Volunteer implements VolunteerPartial {
|
|||||||
|
|
||||||
password2 = ""
|
password2 = ""
|
||||||
|
|
||||||
pushNotifSubscription = ""
|
|
||||||
|
|
||||||
acceptsNotifs = ""
|
acceptsNotifs = ""
|
||||||
|
|
||||||
team2022 = 0
|
team2022 = 0
|
||||||
@ -128,7 +126,6 @@ export const translationVolunteer: { [k in keyof Volunteer]: string } = {
|
|||||||
created: "creation",
|
created: "creation",
|
||||||
password1: "passe1",
|
password1: "passe1",
|
||||||
password2: "passe2",
|
password2: "passe2",
|
||||||
pushNotifSubscription: "pushNotifSubscription",
|
|
||||||
acceptsNotifs: "accepteLesNotifs",
|
acceptsNotifs: "accepteLesNotifs",
|
||||||
team2022: "équipe2022",
|
team2022: "équipe2022",
|
||||||
ok: "OK",
|
ok: "OK",
|
||||||
@ -194,7 +191,6 @@ export const volunteerExample: Volunteer = {
|
|||||||
created: new Date(0),
|
created: new Date(0),
|
||||||
password1: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPkdq9d5fqpbl8ASimSjNj4SR.9O",
|
password1: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPkdq9d5fqpbl8ASimSjNj4SR.9O",
|
||||||
password2: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPkdq9d5fqpbl8ASimSjNj4SR.9O",
|
password2: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPkdq9d5fqpbl8ASimSjNj4SR.9O",
|
||||||
pushNotifSubscription: "",
|
|
||||||
acceptsNotifs: "",
|
acceptsNotifs: "",
|
||||||
team2022: 0,
|
team2022: 0,
|
||||||
ok: [5, 7, 24, 26, 31, 38, 50, 52, 54, 58],
|
ok: [5, 7, 24, 26, 31, 38, 50, 52, 54, 58],
|
||||||
@ -248,7 +244,6 @@ export interface VolunteerAsks {
|
|||||||
id: Volunteer["id"]
|
id: Volunteer["id"]
|
||||||
firstname: Volunteer["firstname"]
|
firstname: Volunteer["firstname"]
|
||||||
hiddenAsks: Volunteer["hiddenAsks"]
|
hiddenAsks: Volunteer["hiddenAsks"]
|
||||||
pushNotifSubscription: Volunteer["pushNotifSubscription"]
|
|
||||||
acceptsNotifs: Volunteer["acceptsNotifs"]
|
acceptsNotifs: Volunteer["acceptsNotifs"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ interface Arg {
|
|||||||
// Use inferred return type for making correctly Redux types
|
// Use inferred return type for making correctly Redux types
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
const createStore = ({ initialState, url, jwt, id, roles }: Arg = {}) => {
|
const createStore = ({ initialState, url, jwt, id, roles }: Arg = {}) => {
|
||||||
const history = __SERVER__
|
const history = SSR
|
||||||
? createMemoryHistory({ initialEntries: [url || "/"] })
|
? createMemoryHistory({ initialEntries: [url || "/"] })
|
||||||
: createBrowserHistory()
|
: createBrowserHistory()
|
||||||
const store = configureStore({
|
const store = configureStore({
|
||||||
@ -30,7 +30,7 @@ const createStore = ({ initialState, url, jwt, id, roles }: Arg = {}) => {
|
|||||||
...getDefaultMiddleware(),
|
...getDefaultMiddleware(),
|
||||||
routerMiddleware(history),
|
routerMiddleware(history),
|
||||||
],
|
],
|
||||||
devTools: __DEV__,
|
devTools: DEV,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (jwt && id && roles) {
|
if (jwt && id && roles) {
|
||||||
|
22
src/types/index.d.ts
vendored
22
src/types/index.d.ts
vendored
@ -1,9 +1,8 @@
|
|||||||
declare const __CLIENT__: boolean
|
declare const API_URL: string
|
||||||
declare const __SERVER__: boolean
|
declare const SSR: boolean
|
||||||
declare const __DEV__: boolean
|
declare const DEV: boolean
|
||||||
declare const __LOCAL__: boolean
|
declare const REGISTER_DISCORD_COMMANDS: boolean
|
||||||
declare const __REGISTER_DISCORD_COMMANDS__: boolean
|
declare const TEST: boolean
|
||||||
declare const __TEST__: boolean
|
|
||||||
|
|
||||||
declare module "*.svg"
|
declare module "*.svg"
|
||||||
declare module "*.gif"
|
declare module "*.gif"
|
||||||
@ -16,12 +15,11 @@ declare module "*.scss"
|
|||||||
|
|
||||||
declare namespace NodeJS {
|
declare namespace NodeJS {
|
||||||
interface Global {
|
interface Global {
|
||||||
__CLIENT__: boolean
|
API_URL: string
|
||||||
__SERVER__: boolean
|
SSR: boolean
|
||||||
__DEV__: boolean
|
DEV: boolean
|
||||||
__LOCAL__: boolean
|
REGISTER_DISCORD_COMMANDS: boolean
|
||||||
__REGISTER_DISCORD_COMMANDS__: boolean
|
TEST: boolean
|
||||||
__TEST__: boolean
|
|
||||||
$RefreshReg$: () => void
|
$RefreshReg$: () => void
|
||||||
$RefreshSig$$: () => void
|
$RefreshSig$$: () => void
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,7 @@ import LoadablePlugin from "@loadable/webpack-plugin"
|
|||||||
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer"
|
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer"
|
||||||
|
|
||||||
export const isDev = process.env.NODE_ENV === "development"
|
export const isDev = process.env.NODE_ENV === "development"
|
||||||
const isLocal = process.env.LOCAL === "true"
|
|
||||||
const isRegisterDiscordCommands = process.env.REGISTER_DISCORD_COMMANDS === "true"
|
|
||||||
const getStyleLoaders = (isWeb: boolean, isSass?: boolean) => {
|
const getStyleLoaders = (isWeb: boolean, isSass?: boolean) => {
|
||||||
let loaders: RuleSetUseItem[] = [
|
let loaders: RuleSetUseItem[] = [
|
||||||
{
|
{
|
||||||
@ -46,11 +45,10 @@ const getPlugins = (isWeb: boolean) => {
|
|||||||
}),
|
}),
|
||||||
// Setting global variables
|
// Setting global variables
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
__CLIENT__: isWeb,
|
SSR: !isWeb,
|
||||||
__SERVER__: !isWeb,
|
DEV: isDev,
|
||||||
__DEV__: isDev,
|
REGISTER_DISCORD_COMMANDS: Boolean(process.env.REGISTER_DISCORD_COMMANDS),
|
||||||
__LOCAL__: isLocal,
|
API_URL: JSON.stringify(process.env.API_URL),
|
||||||
__REGISTER_DISCORD_COMMANDS__: isRegisterDiscordCommands,
|
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
72
yarn.lock
72
yarn.lock
@ -2212,11 +2212,6 @@
|
|||||||
"@types/mime" "*"
|
"@types/mime" "*"
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/serviceworker@^0.0.32":
|
|
||||||
version "0.0.32"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/serviceworker/-/serviceworker-0.0.32.tgz#c262fed4e394f7f3e3787a597f8ecb5c12161626"
|
|
||||||
integrity sha512-3B1uuyZQ86m9C3BBhNfl4mJs+Whdm1ozPyeLm1Z5hX4cKH0JLPfD9CBFEfdXmCMeNdbcJGfBZXYNXzlrpVrwQg==
|
|
||||||
|
|
||||||
"@types/source-list-map@*":
|
"@types/source-list-map@*":
|
||||||
version "0.1.6"
|
version "0.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.6.tgz#164e169dd061795b50b83c19e4d3be09f8d3a454"
|
resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.6.tgz#164e169dd061795b50b83c19e4d3be09f8d3a454"
|
||||||
@ -2258,13 +2253,6 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.10.tgz#04ffa7f406ab628f7f7e97ca23e290cd8ab15efc"
|
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.10.tgz#04ffa7f406ab628f7f7e97ca23e290cd8ab15efc"
|
||||||
integrity sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==
|
integrity sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==
|
||||||
|
|
||||||
"@types/web-push@^3.3.2":
|
|
||||||
version "3.6.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/web-push/-/web-push-3.6.3.tgz#7698cdeeabd70d1129a6e02bd58af1e985cdfa03"
|
|
||||||
integrity sha512-v3oT4mMJsHeJ/rraliZ+7TbZtr5bQQuxcgD7C3/1q/zkAj29c8RE0F9lVZVu3hiQe5Z9fYcBreV7TLnfKR+4mg==
|
|
||||||
dependencies:
|
|
||||||
"@types/node" "*"
|
|
||||||
|
|
||||||
"@types/webpack-bundle-analyzer@^4.4.1":
|
"@types/webpack-bundle-analyzer@^4.4.1":
|
||||||
version "4.6.3"
|
version "4.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.6.3.tgz#53c26f21134ca2e5049fd2af4f2ffbf8dfe87b4f"
|
resolved "https://registry.yarnpkg.com/@types/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.6.3.tgz#53c26f21134ca2e5049fd2af4f2ffbf8dfe87b4f"
|
||||||
@ -2651,13 +2639,6 @@ agent-base@6:
|
|||||||
dependencies:
|
dependencies:
|
||||||
debug "4"
|
debug "4"
|
||||||
|
|
||||||
agent-base@^7.0.2:
|
|
||||||
version "7.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434"
|
|
||||||
integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==
|
|
||||||
dependencies:
|
|
||||||
debug "^4.3.4"
|
|
||||||
|
|
||||||
aggregate-error@^3.0.0:
|
aggregate-error@^3.0.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
|
resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
|
||||||
@ -2927,16 +2908,6 @@ arrify@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa"
|
resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa"
|
||||||
integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==
|
integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==
|
||||||
|
|
||||||
asn1.js@^5.3.0:
|
|
||||||
version "5.4.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
|
|
||||||
integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==
|
|
||||||
dependencies:
|
|
||||||
bn.js "^4.0.0"
|
|
||||||
inherits "^2.0.1"
|
|
||||||
minimalistic-assert "^1.0.0"
|
|
||||||
safer-buffer "^2.1.0"
|
|
||||||
|
|
||||||
ast-types-flow@^0.0.8:
|
ast-types-flow@^0.0.8:
|
||||||
version "0.0.8"
|
version "0.0.8"
|
||||||
resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6"
|
resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6"
|
||||||
@ -3221,11 +3192,6 @@ bl@^1.0.0:
|
|||||||
readable-stream "^2.3.5"
|
readable-stream "^2.3.5"
|
||||||
safe-buffer "^5.1.1"
|
safe-buffer "^5.1.1"
|
||||||
|
|
||||||
bn.js@^4.0.0:
|
|
||||||
version "4.12.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
|
|
||||||
integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
|
|
||||||
|
|
||||||
body-parser@1.20.1:
|
body-parser@1.20.1:
|
||||||
version "1.20.1"
|
version "1.20.1"
|
||||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
|
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
|
||||||
@ -6146,13 +6112,6 @@ http-proxy-agent@^4.0.1:
|
|||||||
agent-base "6"
|
agent-base "6"
|
||||||
debug "4"
|
debug "4"
|
||||||
|
|
||||||
http_ece@1.1.0:
|
|
||||||
version "1.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/http_ece/-/http_ece-1.1.0.tgz#74780c6eb32d8ddfe9e36a83abcd81fe0cd4fb75"
|
|
||||||
integrity sha512-bptAfCDdPJxOs5zYSe7Y3lpr772s1G346R4Td5LgRUeCwIGpCGDUTJxRrhTNcAXbx37spge0kWEIH7QAYWNTlA==
|
|
||||||
dependencies:
|
|
||||||
urlsafe-base64 "~1.0.0"
|
|
||||||
|
|
||||||
https-proxy-agent@^5.0.0:
|
https-proxy-agent@^5.0.0:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
|
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
|
||||||
@ -6161,14 +6120,6 @@ https-proxy-agent@^5.0.0:
|
|||||||
agent-base "6"
|
agent-base "6"
|
||||||
debug "4"
|
debug "4"
|
||||||
|
|
||||||
https-proxy-agent@^7.0.0:
|
|
||||||
version "7.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz#e2645b846b90e96c6e6f347fb5b2e41f1590b09b"
|
|
||||||
integrity sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==
|
|
||||||
dependencies:
|
|
||||||
agent-base "^7.0.2"
|
|
||||||
debug "4"
|
|
||||||
|
|
||||||
https@^1.0.0:
|
https@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/https/-/https-1.0.0.tgz#3c37c7ae1a8eeb966904a2ad1e975a194b7ed3a4"
|
resolved "https://registry.yarnpkg.com/https/-/https-1.0.0.tgz#3c37c7ae1a8eeb966904a2ad1e975a194b7ed3a4"
|
||||||
@ -8031,11 +7982,6 @@ mini-css-extract-plugin@^2.7.7:
|
|||||||
dependencies:
|
dependencies:
|
||||||
schema-utils "^4.0.0"
|
schema-utils "^4.0.0"
|
||||||
|
|
||||||
minimalistic-assert@^1.0.0:
|
|
||||||
version "1.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
|
|
||||||
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
|
|
||||||
|
|
||||||
minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:
|
minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:
|
||||||
version "3.1.2"
|
version "3.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||||
@ -10026,7 +9972,7 @@ safe-regex-test@^1.0.0:
|
|||||||
get-intrinsic "^1.1.3"
|
get-intrinsic "^1.1.3"
|
||||||
is-regex "^1.1.4"
|
is-regex "^1.1.4"
|
||||||
|
|
||||||
"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.1.0:
|
"safer-buffer@>= 2.1.2 < 3":
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||||
@ -11377,11 +11323,6 @@ url-to-options@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
|
resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
|
||||||
integrity sha512-0kQLIzG4fdk/G5NONku64rSH/x32NOA39LVQqlK8Le6lvTF6GGRJpqaQFGgU+CLwySIqBSMdwYM0sYcW9f6P4A==
|
integrity sha512-0kQLIzG4fdk/G5NONku64rSH/x32NOA39LVQqlK8Le6lvTF6GGRJpqaQFGgU+CLwySIqBSMdwYM0sYcW9f6P4A==
|
||||||
|
|
||||||
urlsafe-base64@~1.0.0:
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/urlsafe-base64/-/urlsafe-base64-1.0.0.tgz#23f89069a6c62f46cf3a1d3b00169cefb90be0c6"
|
|
||||||
integrity sha512-RtuPeMy7c1UrHwproMZN9gN6kiZ0SvJwRaEzwZY0j9MypEkFqyBaKv176jvlPtg58Zh36bOkS0NFABXMHvvGCA==
|
|
||||||
|
|
||||||
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
@ -11493,17 +11434,6 @@ watchpack@^2.4.0:
|
|||||||
glob-to-regexp "^0.4.1"
|
glob-to-regexp "^0.4.1"
|
||||||
graceful-fs "^4.1.2"
|
graceful-fs "^4.1.2"
|
||||||
|
|
||||||
web-push@^3.4.5:
|
|
||||||
version "3.6.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/web-push/-/web-push-3.6.6.tgz#592facb26116187136b0d543768ef85675791ac8"
|
|
||||||
integrity sha512-SyteEck9fiCskNmPxs/GFhJsZrIyLfRvjWNmcUwULLJyCU0f1oxo2sWTokXA1mDAq9vxk4e4gVcb/8agq73NkQ==
|
|
||||||
dependencies:
|
|
||||||
asn1.js "^5.3.0"
|
|
||||||
http_ece "1.1.0"
|
|
||||||
https-proxy-agent "^7.0.0"
|
|
||||||
jws "^4.0.0"
|
|
||||||
minimist "^1.2.5"
|
|
||||||
|
|
||||||
webidl-conversions@^3.0.0:
|
webidl-conversions@^3.0.0:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user