Skip to content

Commit

Permalink
Expose the express depencendencies by extracting it to a seperate module
Browse files Browse the repository at this point in the history
Refs: #1834
  • Loading branch information
erkannt committed Sep 4, 2024
1 parent a1f6c1a commit 42f6830
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 94 deletions.
107 changes: 107 additions & 0 deletions src/ExpressServer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import KeyvRedis from '@keyv/redis'
import { SystemClock } from 'clock-ts'
import { Effect } from 'effect'
import * as C from 'fp-ts/lib/Console.js'
import { pipe } from 'fp-ts/lib/function.js'
import type { Redis } from 'ioredis'
import Keyv from 'keyv'
import * as L from 'logger-fp-ts'
import fetch from 'make-fetch-happen'
import nodemailer from 'nodemailer'
import { P, match } from 'ts-pattern'
import { app } from './app.js'
import { decodeEnv } from './env.js'

export const expressServer = (redis: Redis) => {
const env = decodeEnv(process)()
const loggerEnv: L.LoggerEnv = {
clock: SystemClock,
logger: pipe(C.log, L.withShow(env.LOG_FORMAT === 'json' ? L.JsonShowLogEntry : L.getColoredShow(L.ShowLogEntry))),
}
const sendMailEnv = match(env)
.with({ MAILJET_API_KEY: P.string }, env => ({
mailjetApi: {
key: env.MAILJET_API_KEY,
secret: env.MAILJET_API_SECRET,
sandbox: env.MAILJET_API_SANDBOX,
},
}))
.with({ SMTP_URI: P.instanceOf(URL) }, env => ({
nodemailer: nodemailer.createTransport(env.SMTP_URI.href),
}))
.exhaustive()
const createKeyvStore = () => new KeyvRedis(redis)

return Effect.succeed(
app({
...loggerEnv,
allowSiteCrawlers: env.ALLOW_SITE_CRAWLERS,
authorInviteStore: new Keyv({ namespace: 'author-invite', store: createKeyvStore() }),
avatarStore: new Keyv({ namespace: 'avatar-store', store: createKeyvStore() }),
canConnectOrcidProfile: () => true,
canRequestReviews: () => true,
canSeeGatesLogo: true,
canUploadAvatar: () => true,
canUseSearchQueries: () => true,
cloudinaryApi: { cloudName: 'prereview', key: env.CLOUDINARY_API_KEY, secret: env.CLOUDINARY_API_SECRET },
coarNotifyToken: env.COAR_NOTIFY_TOKEN,
coarNotifyUrl: env.COAR_NOTIFY_URL,
contactEmailAddressStore: new Keyv({ namespace: 'contact-email-address', store: createKeyvStore() }),
environmentLabel: env.ENVIRONMENT_LABEL,
fathomId: env.FATHOM_SITE_ID,
fetch: fetch.defaults({
cachePath: 'data/cache',
headers: {
'User-Agent': `PREreview (${env.PUBLIC_URL.href}; mailto:[email protected])`,
},
}),
formStore: new Keyv({ namespace: 'forms', store: createKeyvStore() }),
careerStageStore: new Keyv({ namespace: 'career-stage', store: createKeyvStore() }),
ghostApi: {
key: env.GHOST_API_KEY,
},
isOpenForRequestsStore: new Keyv({ namespace: 'is-open-for-requests', store: createKeyvStore() }),
isUserBlocked: user => env.BLOCKED_USERS.includes(user),
legacyPrereviewApi: {
app: env.LEGACY_PREREVIEW_API_APP,
key: env.LEGACY_PREREVIEW_API_KEY,
url: env.LEGACY_PREREVIEW_URL,
update: env.LEGACY_PREREVIEW_UPDATE,
},
languagesStore: new Keyv({ namespace: 'languages', store: createKeyvStore() }),
locationStore: new Keyv({ namespace: 'location', store: createKeyvStore() }),
...sendMailEnv,
orcidApiUrl: env.ORCID_API_URL,
orcidApiToken: env.ORCID_API_READ_PUBLIC_TOKEN,
orcidOauth: {
authorizeUrl: new URL(`${env.ORCID_URL.origin}/oauth/authorize`),
clientId: env.ORCID_CLIENT_ID,
clientSecret: env.ORCID_CLIENT_SECRET,
revokeUrl: new URL(`${env.ORCID_URL.origin}/oauth/revoke`),
tokenUrl: new URL(`${env.ORCID_URL.origin}/oauth/token`),
},
orcidTokenStore: new Keyv({ namespace: 'orcid-token', store: createKeyvStore() }),
publicUrl: env.PUBLIC_URL,
redis,
researchInterestsStore: new Keyv({ namespace: 'research-interests', store: createKeyvStore() }),
reviewRequestStore: new Keyv({ namespace: 'review-request', store: createKeyvStore() }),
scietyListToken: env.SCIETY_LIST_TOKEN,
secret: env.SECRET,
sessionCookie: 'session',
sessionStore: new Keyv({ namespace: 'sessions', store: createKeyvStore(), ttl: 1000 * 60 * 60 * 24 * 30 }),
slackOauth: {
authorizeUrl: new URL('https://slack.com/oauth/v2/authorize'),
clientId: env.SLACK_CLIENT_ID,
clientSecret: env.SLACK_CLIENT_SECRET,
tokenUrl: new URL('https://slack.com/api/oauth.v2.access'),
},
slackApiToken: env.SLACK_API_TOKEN,
slackApiUpdate: env.SLACK_UPDATE,
slackUserIdStore: new Keyv({ namespace: 'slack-user-id', store: createKeyvStore() }),
userOnboardingStore: new Keyv({ namespace: 'user-onboarding', store: createKeyvStore() }),
wasPrereviewRemoved: id => env.REMOVED_PREREVIEWS.includes(id),
zenodoApiKey: env.ZENODO_API_KEY,
zenodoUrl: env.ZENODO_URL,
}),
)
}
97 changes: 3 additions & 94 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { NodeRuntime } from '@effect/platform-node'
import KeyvRedis from '@keyv/redis'
import cacache from 'cacache'
import { SystemClock } from 'clock-ts'
import * as dns from 'dns'
Expand All @@ -9,13 +8,10 @@ import * as E from 'fp-ts/lib/Either.js'
import { pipe } from 'fp-ts/lib/function.js'
import type { JsonRecord } from 'fp-ts/lib/Json.js'
import { Redis } from 'ioredis'
import Keyv from 'keyv'
import * as L from 'logger-fp-ts'
import fetch from 'make-fetch-happen'
import nodemailer from 'nodemailer'
import { P, match } from 'ts-pattern'
import { app } from './app.js'
import type { app } from './app.js'
import { decodeEnv } from './env.js'
import { expressServer } from './ExpressServer.js'

const env = decodeEnv(process)()

Expand All @@ -25,7 +21,6 @@ const loggerEnv: L.LoggerEnv = {
}

const redis = new Redis(env.REDIS_URI.href, { commandTimeout: 2 * 1000, enableAutoPipelining: true })
const createKeyvStore = () => new KeyvRedis(redis)

redis.on('connect', () => L.debug('Redis connected')(loggerEnv)())
redis.on('close', () => L.debug('Redis connection closed')(loggerEnv)())
Expand All @@ -37,92 +32,6 @@ if (env.ZENODO_URL.href.includes('sandbox')) {
dns.setDefaultResultOrder('ipv4first')
}

const sendMailEnv = match(env)
.with({ MAILJET_API_KEY: P.string }, env => ({
mailjetApi: {
key: env.MAILJET_API_KEY,
secret: env.MAILJET_API_SECRET,
sandbox: env.MAILJET_API_SANDBOX,
},
}))
.with({ SMTP_URI: P.instanceOf(URL) }, env => ({
nodemailer: nodemailer.createTransport(env.SMTP_URI.href),
}))
.exhaustive()

const expressServer = Effect.succeed(
app({
...loggerEnv,
allowSiteCrawlers: env.ALLOW_SITE_CRAWLERS,
authorInviteStore: new Keyv({ namespace: 'author-invite', store: createKeyvStore() }),
avatarStore: new Keyv({ namespace: 'avatar-store', store: createKeyvStore() }),
canConnectOrcidProfile: () => true,
canRequestReviews: () => true,
canSeeGatesLogo: true,
canUploadAvatar: () => true,
canUseSearchQueries: () => true,
cloudinaryApi: { cloudName: 'prereview', key: env.CLOUDINARY_API_KEY, secret: env.CLOUDINARY_API_SECRET },
coarNotifyToken: env.COAR_NOTIFY_TOKEN,
coarNotifyUrl: env.COAR_NOTIFY_URL,
contactEmailAddressStore: new Keyv({ namespace: 'contact-email-address', store: createKeyvStore() }),
environmentLabel: env.ENVIRONMENT_LABEL,
fathomId: env.FATHOM_SITE_ID,
fetch: fetch.defaults({
cachePath: 'data/cache',
headers: {
'User-Agent': `PREreview (${env.PUBLIC_URL.href}; mailto:[email protected])`,
},
}),
formStore: new Keyv({ namespace: 'forms', store: createKeyvStore() }),
careerStageStore: new Keyv({ namespace: 'career-stage', store: createKeyvStore() }),
ghostApi: {
key: env.GHOST_API_KEY,
},
isOpenForRequestsStore: new Keyv({ namespace: 'is-open-for-requests', store: createKeyvStore() }),
isUserBlocked: user => env.BLOCKED_USERS.includes(user),
legacyPrereviewApi: {
app: env.LEGACY_PREREVIEW_API_APP,
key: env.LEGACY_PREREVIEW_API_KEY,
url: env.LEGACY_PREREVIEW_URL,
update: env.LEGACY_PREREVIEW_UPDATE,
},
languagesStore: new Keyv({ namespace: 'languages', store: createKeyvStore() }),
locationStore: new Keyv({ namespace: 'location', store: createKeyvStore() }),
...sendMailEnv,
orcidApiUrl: env.ORCID_API_URL,
orcidApiToken: env.ORCID_API_READ_PUBLIC_TOKEN,
orcidOauth: {
authorizeUrl: new URL(`${env.ORCID_URL.origin}/oauth/authorize`),
clientId: env.ORCID_CLIENT_ID,
clientSecret: env.ORCID_CLIENT_SECRET,
revokeUrl: new URL(`${env.ORCID_URL.origin}/oauth/revoke`),
tokenUrl: new URL(`${env.ORCID_URL.origin}/oauth/token`),
},
orcidTokenStore: new Keyv({ namespace: 'orcid-token', store: createKeyvStore() }),
publicUrl: env.PUBLIC_URL,
redis,
researchInterestsStore: new Keyv({ namespace: 'research-interests', store: createKeyvStore() }),
reviewRequestStore: new Keyv({ namespace: 'review-request', store: createKeyvStore() }),
scietyListToken: env.SCIETY_LIST_TOKEN,
secret: env.SECRET,
sessionCookie: 'session',
sessionStore: new Keyv({ namespace: 'sessions', store: createKeyvStore(), ttl: 1000 * 60 * 60 * 24 * 30 }),
slackOauth: {
authorizeUrl: new URL('https://slack.com/oauth/v2/authorize'),
clientId: env.SLACK_CLIENT_ID,
clientSecret: env.SLACK_CLIENT_SECRET,
tokenUrl: new URL('https://slack.com/api/oauth.v2.access'),
},
slackApiToken: env.SLACK_API_TOKEN,
slackApiUpdate: env.SLACK_UPDATE,
slackUserIdStore: new Keyv({ namespace: 'slack-user-id', store: createKeyvStore() }),
userOnboardingStore: new Keyv({ namespace: 'user-onboarding', store: createKeyvStore() }),
wasPrereviewRemoved: id => env.REMOVED_PREREVIEWS.includes(id),
zenodoApiKey: env.ZENODO_API_KEY,
zenodoUrl: env.ZENODO_URL,
}),
)

if (env.VERIFY_CACHE) {
void Promise.resolve()
.then(() => L.debug('Verifying cache')(loggerEnv)())
Expand Down Expand Up @@ -158,6 +67,6 @@ pipe(
expressServerLifecycle,
Layer.scopedDiscard,
Layer.launch,
Effect.provideServiceEffect(Express, expressServer),
Effect.provideServiceEffect(Express, expressServer(redis)),
NodeRuntime.runMain,
)

0 comments on commit 42f6830

Please sign in to comment.