🔧 use .env instead of json config files

This commit is contained in:
ChatonDeAru 2024-03-24 00:01:09 +01:00 committed by ChatonDeAru (Romain)
parent f88dbc36d2
commit 24db804c7f
16 changed files with 121 additions and 166 deletions

18
.env.example Normal file
View File

@ -0,0 +1,18 @@
## Google Cloud Platform Service Account
GCP_SERVICE_ACCOUNT_PRIVATE_KEY=
GCP_SERVICE_ACCOUNT_CLIENT_ID=
GCP_SERVICE_ACCOUNT_CLIENT_EMAIL=
GSHEET_ID=
## Discord
DISCORD_TOKEN=
DISCORD_CLIENTID=
DISCORD_GUILDID=
## Notifications
FORCE_ORANGE_PUBLIC_VAPID_KEY=
FORCE_ORANGE_PRIVATE_VAPID_KEY=
## Environment
PORT=8080
API_URL=http://fo.parisestludique.fr/api

View File

@ -60,5 +60,6 @@ module.exports = {
__LOCAL__: false, __LOCAL__: false,
__REGISTER_DISCORD_COMMANDS__: false, __REGISTER_DISCORD_COMMANDS__: false,
__TEST__: false, __TEST__: false,
API_URL: false,
}, },
} }

View File

@ -24,6 +24,7 @@ module.exports = {
__LOCAL__: false, __LOCAL__: false,
__REGISTER_DISCORD_COMMANDS__: false, __REGISTER_DISCORD_COMMANDS__: false,
__TEST__: true, __TEST__: true,
API_URL: "http://localhost:3000",
}, },
maxConcurrency: 50, maxConcurrency: 50,
maxWorkers: 1, maxWorkers: 1,

View File

@ -39,12 +39,12 @@
"npm": ">=6" "npm": ">=6"
}, },
"scripts": { "scripts": {
"dev": "yarn dev:build && nodemon ./public/server", "dev": "yarn dev:build && nodemon -r dotenv/config ./public/server",
"ser": "yarn dev:build && node ./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",
@ -174,6 +174,7 @@
"compression-webpack-plugin": "^8.0.1", "compression-webpack-plugin": "^8.0.1",
"css-loader": "^5.2.6", "css-loader": "^5.2.6",
"css-minimizer-webpack-plugin": "^3.0.2", "css-minimizer-webpack-plugin": "^3.0.2",
"dotenv": "^16.4.5",
"eslint": "^7.14.0", "eslint": "^7.14.0",
"eslint-config-airbnb": "^18.2.1", "eslint-config-airbnb": "^18.2.1",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.3.0",

View File

@ -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"
@ -24,9 +23,22 @@ const App = ({ route, location }: Route): JSX.Element => {
} }
// else // else
const appInfo = {
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.",
},
],
}
return ( return (
<div> <div>
<Helmet {...config.APP}> <Helmet {...appInfo}>
<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="/">{appInfo.title}</a>
</h1> </h1>
<div className={styles.siteDescription}>{config.APP.description}</div> <div className={styles.siteDescription}>{appInfo.description}</div>
</div> </div>
<div className={styles.menuWrapper}> <div className={styles.menuWrapper}>
<MainMenu /> <MainMenu />

View File

@ -1,20 +0,0 @@
export default {
HOST: "localhost",
PORT: 3000,
API_URL: "http://localhost:3000",
GOOGLE_SHEET_ID: "1g-GB1NHLtUAQnQkbyo_5Lv6GzJ58DeOsUTrHlIVi01M",
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!",
}

View File

@ -1,4 +0,0 @@
import defaultConfig from "./default"
import prodConfig from "./prod"
export default __DEV__ ? defaultConfig : { ...defaultConfig, ...prodConfig }

View File

@ -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,
}

View File

@ -13,8 +13,6 @@ import {
User, User,
PartialUser, PartialUser,
} from "discord.js" } from "discord.js"
import { promises as fs, constants } from "fs"
import path from "path"
import { translationVolunteer, Volunteer, VolunteerWithoutId } from "../services/volunteers" import { translationVolunteer, Volunteer, VolunteerWithoutId } from "../services/volunteers"
import { import {
@ -23,13 +21,8 @@ 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 const { DISCORD_TOKEN, DISCORD_CLIENTID, DISCORD_GUILDID } = process.env
// let cachedClientId: string
let cachedGuildId: string
const CREDS_PATH = path.resolve(process.cwd(), "access/discordToken.json")
getCreds() // Necessary until we can make async express middleware
type Command = { type Command = {
data: SlashCommandBuilder data: SlashCommandBuilder
@ -52,19 +45,8 @@ const commands: Collection<string, Command> = new Collection()
// }, // },
// } // }
let hasDiscordAccessReturn: boolean | undefined export function hasDiscordAccess(): boolean {
export async function hasDiscordAccess(): Promise<boolean> { return Boolean(DISCORD_TOKEN && DISCORD_CLIENTID && DISCORD_GUILDID)
if (hasDiscordAccessReturn !== undefined) {
return hasDiscordAccessReturn
}
try {
// eslint-disable-next-line no-bitwise
await fs.access(CREDS_PATH, constants.R_OK | constants.W_OK)
hasDiscordAccessReturn = true
} catch {
hasDiscordAccessReturn = false
}
return hasDiscordAccessReturn
} }
// export async function discordRegisterCommands(): Promise<void> { // export async function discordRegisterCommands(): Promise<void> {
@ -72,7 +54,7 @@ export async function hasDiscordAccess(): Promise<boolean> {
// return // return
// } // }
// if (!(await hasDiscordAccess())) { // if (!(hasDiscordAccess())) {
// console.error(`Discord bot: no creds found, not running bot`) // console.error(`Discord bot: no creds found, not running bot`)
// return // return
// } // }
@ -82,11 +64,11 @@ export async function hasDiscordAccess(): Promise<boolean> {
// const commandsToRegister = [] // const commandsToRegister = []
// commandsToRegister.push(userCommand.data.toJSON()) // commandsToRegister.push(userCommand.data.toJSON())
// const rest = new REST({ version: '10' }).setToken(cachedToken) // const rest = new REST({ version: '10' }).setToken(DISCORD_TOKEN)
// try { // try {
// await rest.put( // await rest.put(
// Routes.applicationGuildCommands(cachedClientId, cachedGuildId), // Routes.applicationGuildCommands(DISCORD_CLIENTID, DISCORD_GUILDID),
// { body: commandsToRegister }, // { body: commandsToRegister },
// ) // )
// } catch (error) { // } catch (error) {
@ -102,11 +84,10 @@ export async function discordBot(): Promise<void> {
return return
} }
if (!(await hasDiscordAccess())) { if (!hasDiscordAccess()) {
console.error(`Discord bot: no creds found, not running bot`) console.error(`Discord bot: no creds found, not running bot`)
return return
} }
await getCreds()
const client = new Client({ const client = new Client({
intents: [ intents: [
@ -158,7 +139,7 @@ export async function discordBot(): Promise<void> {
await setRolesFromEmoji(client, user, reaction, "remove") await setRolesFromEmoji(client, user, reaction, "remove")
}) })
client.login(cachedToken) client.login(DISCORD_TOKEN)
} catch (error) { } catch (error) {
console.error("Discord error", error) console.error("Discord error", error)
} }
@ -205,6 +186,8 @@ async function setBotReactions(client: Client) {
async function setAllRoles(client: Client) { async function setAllRoles(client: Client) {
try { try {
if (!DISCORD_GUILDID) return
const volunteerSheet = await getSheet<VolunteerWithoutId, Volunteer>( const volunteerSheet = await getSheet<VolunteerWithoutId, Volunteer>(
"Volunteers", "Volunteers",
new Volunteer(), new Volunteer(),
@ -216,8 +199,7 @@ async function setAllRoles(client: Client) {
} }
const volunteerByDiscordId = _.mapKeys(volunteerList, (v) => v.discordId.toString()) const volunteerByDiscordId = _.mapKeys(volunteerList, (v) => v.discordId.toString())
const guild = await client.guilds.fetch(DISCORD_GUILDID)
const guild = await client.guilds.fetch(cachedGuildId)
if (!guild || !guild.members.cache) { if (!guild || !guild.members.cache) {
return return
@ -356,6 +338,8 @@ async function setRolesFromEmoji(
reaction: MessageReaction | PartialMessageReaction, reaction: MessageReaction | PartialMessageReaction,
action: "add" | "remove" action: "add" | "remove"
) { ) {
if (!DISCORD_GUILDID) return
const discordRolesSheet = await getSheet<DiscordRoleWithoutId, DiscordRole>( const discordRolesSheet = await getSheet<DiscordRoleWithoutId, DiscordRole>(
"DiscordRoles", "DiscordRoles",
new DiscordRole(), new DiscordRole(),
@ -367,7 +351,7 @@ async function setRolesFromEmoji(
} }
await client.guilds.fetch() await client.guilds.fetch()
const guild = client.guilds.resolve(cachedGuildId) const guild = client.guilds.resolve(DISCORD_GUILDID)
if (!guild || !guild.members.cache) { if (!guild || !guild.members.cache) {
return return
@ -408,20 +392,3 @@ async function fetchPartial(reaction: MessageReaction | PartialMessageReaction):
} }
return true return true
} }
async function getCreds(): Promise<void> {
if (!cachedToken) {
try {
const credsContent = await fs.readFile(CREDS_PATH)
const parsedCreds = credsContent && JSON.parse(credsContent.toString())
if (!parsedCreds) {
return
}
cachedToken = parsedCreds.token
// cachedClientId = parsedCreds.clientId
cachedGuildId = parsedCreds.guildId
} catch (e: any) {
cachedToken = config.DEV_DISCORD_TOKEN
}
}
}

View File

@ -1,7 +1,5 @@
// eslint-disable-next-line max-classes-per-file // eslint-disable-next-line max-classes-per-file
import path from "path"
import _, { assign, pick } from "lodash" import _, { assign, pick } from "lodash"
import { promises as fs, constants } from "fs"
import { GoogleSpreadsheet, GoogleSpreadsheetWorksheet } from "google-spreadsheet" import { GoogleSpreadsheet, GoogleSpreadsheetWorksheet } from "google-spreadsheet"
import { SheetNames, saveLocalDb, loadLocalDb } from "./localDb" import { SheetNames, saveLocalDb, loadLocalDb } from "./localDb"
@ -9,14 +7,10 @@ export { SheetNames } from "./localDb"
// Test write attack with: wget --header='Content-Type:application/json' --post-data='{"prenom":"Pierre","nom":"SCELLES","email":"test@gmail.com","telephone":"0601010101","dejaBenevole":false,"commentaire":""}' http://localhost:3000/PostulantAdd // Test write attack with: wget --header='Content-Type:application/json' --post-data='{"prenom":"Pierre","nom":"SCELLES","email":"test@gmail.com","telephone":"0601010101","dejaBenevole":false,"commentaire":""}' http://localhost:3000/PostulantAdd
const CRED_PATH = path.resolve(process.cwd(), "access/gsheets.json")
const REMOTE_UPDATE_DELAY = 120000 const REMOTE_UPDATE_DELAY = 120000
const DELAY_BETWEEN_ATTEMPTS = 30000 const DELAY_BETWEEN_ATTEMPTS = 30000
const DELAY_BETWEEN_FIRST_LOAD = 1500 const DELAY_BETWEEN_FIRST_LOAD = 1500
let creds: string | undefined | null
export type ElementWithId<ElementNoId> = { id: number } & ElementNoId export type ElementWithId<ElementNoId> = { id: number } & ElementNoId
export const sheetNames = new SheetNames() export const sheetNames = new SheetNames()
@ -25,23 +19,16 @@ export const sheetNames = new SheetNames()
type SheetList = { [sheetName in keyof SheetNames]?: Sheet<object, ElementWithId<object>> } type SheetList = { [sheetName in keyof SheetNames]?: Sheet<object, ElementWithId<object>> }
const sheetList: SheetList = {} const sheetList: SheetList = {}
let hasGSheetsAccessReturn: boolean | undefined export function hasGSheetsAccess(): boolean {
export async function hasGSheetsAccess(): Promise<boolean> { return Boolean(
if (hasGSheetsAccessReturn !== undefined) { process.env.GSHEET_ID &&
return hasGSheetsAccessReturn process.env.GCP_SERVICE_ACCOUNT_CLIENT_EMAIL &&
} process.env.GCP_SERVICE_ACCOUNT_PRIVATE_KEY
try { )
// eslint-disable-next-line no-bitwise
await fs.access(CRED_PATH, constants.R_OK | constants.W_OK)
hasGSheetsAccessReturn = true
} catch {
hasGSheetsAccessReturn = false
}
return hasGSheetsAccessReturn
} }
export async function checkGSheetsAccess(): Promise<void> { export async function checkGSheetsAccess(): Promise<void> {
if (!(await hasGSheetsAccess())) { if (!hasGSheetsAccess()) {
console.error(`Google Sheets: no creds found, loading local database instead`) console.error(`Google Sheets: no creds found, loading local database instead`)
} }
} }
@ -367,22 +354,17 @@ export class Sheet<
private async getGSheet(attempts = 3): Promise<GoogleSpreadsheetWorksheet | null> { private async getGSheet(attempts = 3): Promise<GoogleSpreadsheetWorksheet | null> {
return tryNTimes( return tryNTimes(
async () => { async () => {
if (creds === undefined) { if (hasGSheetsAccess()) {
if (await hasGSheetsAccess()) {
const credsBuffer = await fs.readFile(CRED_PATH)
creds = credsBuffer?.toString() || null
} else {
creds = null
}
}
if (creds === null) {
return null
}
// Authentication // Authentication
const doc = new GoogleSpreadsheet("1g-GB1NHLtUAQnQkbyo_5Lv6GzJ58DeOsUTrHlIVi01M") const doc = new GoogleSpreadsheet(process.env.GSHEET_ID)
await doc.useServiceAccountAuth(JSON.parse(creds)) await doc.useServiceAccountAuth({
client_email: process.env.GCP_SERVICE_ACCOUNT_CLIENT_EMAIL || "",
private_key: process.env.GCP_SERVICE_ACCOUNT_PRIVATE_KEY || "",
})
await doc.loadInfo() await doc.loadInfo()
return doc.sheetsByTitle[this.sheetName] return doc.sheetsByTitle[this.sheetName]
}
return null
}, },
() => null, () => null,
attempts, attempts,

View File

@ -49,7 +49,7 @@ 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 { notificationsSubscribe, notificationMain } from "./notifications"
import { /* discordRegisterCommands, */ discordBot, hasDiscordAccess } from "./discordBot" import { /* discordRegisterCommands, */ discordBot, hasDiscordAccess } from "./discordBot"
import checkAccess from "./checkAccess" import checkAccess from "./checkAccess"
@ -202,7 +202,9 @@ if (validCertPath) {
* Listen on provided port, on all network interfaces. * Listen on provided port, on all network interfaces.
*/ */
servers.forEach(({ protocol, server }) => { servers.forEach(({ protocol, server }) => {
server.listen(protocol === "http" ? config.PORT : <number>config.PORT + 2) const port = Number(process.env.PORT) || 3000
server.listen(protocol === "http" ? port : port + 2)
server.on("error", onError) server.on("error", onError)
server.on("listening", () => onListening(server)) server.on("listening", () => onListening(server))
}) })
@ -227,13 +229,11 @@ function onListening(server: any) {
addStatus("Server listening:", chalk.green(`${bind}`)) addStatus("Server listening:", chalk.green(`${bind}`))
} }
hasGSheetsAccess().then((hasApiAccess: boolean) => { if (hasGSheetsAccess()) {
if (hasApiAccess) {
addStatus("Database:", chalk.green(`✅ online from Google Sheet`)) addStatus("Database:", chalk.green(`✅ online from Google Sheet`))
} else { } else {
addStatus("Database:", chalk.blue(`🚧 offline, simulated from local db file`)) addStatus("Database:", chalk.blue(`🚧 offline, simulated from local db file`))
} }
})
const hasSendGridApiAccess = !!process.env.SENDGRID_API_KEY const hasSendGridApiAccess = !!process.env.SENDGRID_API_KEY
if (hasSendGridApiAccess) { if (hasSendGridApiAccess) {
@ -249,13 +249,11 @@ if (hasPushNotifAccess) {
addStatus("Push notif:", chalk.blue(`🚧 offline, simulated`)) addStatus("Push notif:", chalk.blue(`🚧 offline, simulated`))
} }
hasDiscordAccess().then((hasApiAccess: boolean) => { if (hasDiscordAccess()) {
if (hasApiAccess) {
addStatus("Discord bot:", chalk.green(`✅ online through discord.js`)) addStatus("Discord bot:", chalk.green(`✅ online through discord.js`))
} else { } else {
addStatus("Discord bot:", chalk.blue(`🚧 no creds, disabled`)) addStatus("Discord bot:", chalk.blue(`🚧 no creds, disabled`))
} }
})
hasSecret().then((has: boolean) => { hasSecret().then((has: boolean) => {
if (has) { if (has) {

View File

@ -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!"
} }
} }

View File

@ -1,7 +1,6 @@
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 +25,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 +50,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 +76,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 +106,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 +134,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,7 +159,7 @@ 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
) )
@ -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
) )

View File

@ -4,6 +4,7 @@ declare const __DEV__: boolean
declare const __LOCAL__: 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 const API_URL: string
declare module "*.svg" declare module "*.svg"
declare module "*.gif" declare module "*.gif"
@ -22,6 +23,7 @@ declare namespace NodeJS {
__LOCAL__: boolean __LOCAL__: boolean
__REGISTER_DISCORD_COMMANDS__: boolean __REGISTER_DISCORD_COMMANDS__: boolean
__TEST__: boolean __TEST__: boolean
API_URL: string
$RefreshReg$: () => void $RefreshReg$: () => void
$RefreshSig$$: () => void $RefreshSig$$: () => void
} }
@ -30,6 +32,13 @@ declare namespace NodeJS {
SENDGRID_API_KEY?: string SENDGRID_API_KEY?: string
FORCE_ORANGE_PUBLIC_VAPID_KEY?: string FORCE_ORANGE_PUBLIC_VAPID_KEY?: string
FORCE_ORANGE_PRIVATE_VAPID_KEY?: string FORCE_ORANGE_PRIVATE_VAPID_KEY?: string
PORT?: string
HOST?: string
API_URL?: string
GCP_SERVICE_ACCOUNT_CLIENT_ID?: string
GCP_SERVICE_ACCOUNT_PRIVATE_KEY?: string
GCP_SERVICE_ACCOUNT_CLIENT_EMAIL?: string
GSHEET_ID?: string
} }
} }

View File

@ -10,6 +10,7 @@ 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 isLocal = process.env.LOCAL === "true"
const isRegisterDiscordCommands = process.env.REGISTER_DISCORD_COMMANDS === "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[] = [
{ {
@ -51,6 +52,7 @@ const getPlugins = (isWeb: boolean) => {
__DEV__: isDev, __DEV__: isDev,
__LOCAL__: isLocal, __LOCAL__: isLocal,
__REGISTER_DISCORD_COMMANDS__: isRegisterDiscordCommands, __REGISTER_DISCORD_COMMANDS__: isRegisterDiscordCommands,
API_URL: process.env.API_URL || "'http://localhost:3000'",
}), }),
] ]

View File

@ -4186,6 +4186,11 @@ domutils@^2.8.0:
domelementtype "^2.2.0" domelementtype "^2.2.0"
domhandler "^4.2.0" domhandler "^4.2.0"
dotenv@^16.4.5:
version "16.4.5"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f"
integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==
download@^6.2.2: download@^6.2.2:
version "6.2.5" version "6.2.5"
resolved "https://registry.yarnpkg.com/download/-/download-6.2.5.tgz#acd6a542e4cd0bb42ca70cfc98c9e43b07039714" resolved "https://registry.yarnpkg.com/download/-/download-6.2.5.tgz#acd6a542e4cd0bb42ca70cfc98c9e43b07039714"