Skip to content

Commit

Permalink
Use Effect for an environment variable
Browse files Browse the repository at this point in the history
Refs #1834
  • Loading branch information
thewilkybarkid committed Dec 20, 2024
1 parent 506ec78 commit b392b1f
Show file tree
Hide file tree
Showing 6 changed files with 20 additions and 15 deletions.
10 changes: 5 additions & 5 deletions integration/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { FetchHttpClient } from '@effect/platform'
import { NodeHttpServer } from '@effect/platform-node'
import { LibsqlClient } from '@effect/sql-libsql'
import {
test as baseTest,
expect,
type Fixtures,
type PlaywrightTestArgs,
type PlaywrightTestOptions,
test as baseTest,
expect,
} from '@playwright/test'
import { SystemClock } from 'clock-ts'
import { Doi } from 'doi-ts'
import { Effect, Logger as EffectLogger, Fiber, Layer, Option, pipe } from 'effect'
import { Effect, Logger as EffectLogger, Fiber, Layer, Option, pipe, Redacted } from 'effect'
import fetchMock from 'fetch-mock'
import * as fs from 'fs/promises'
import http from 'http'
Expand Down Expand Up @@ -39,7 +39,7 @@ import {
UnverifiedContactEmailAddress,
VerifiedContactEmailAddress,
} from '../src/contact-email-address.js'
import { DeprecatedLoggerEnv, ExpressConfig } from '../src/Context.js'
import { DeprecatedLoggerEnv, ExpressConfig, SessionSecret } from '../src/Context.js'
import { DeprecatedLogger } from '../src/DeprecatedServices.js'
import { createAuthorInviteEmail } from '../src/email.js'
import type {
Expand Down Expand Up @@ -1297,7 +1297,6 @@ const appFixtures: Fixtures<AppFixtures, Record<never, never>, PlaywrightTestArg
researchInterestsStore,
reviewRequestStore,
scietyListToken: 'secret' as NonEmptyString,
secret: '',
sessionCookie: 'session',
sessionStore: new Keyv(),
slackApiToken: '',
Expand Down Expand Up @@ -1325,6 +1324,7 @@ const appFixtures: Fixtures<AppFixtures, Record<never, never>, PlaywrightTestArg
Effect.provideService(GhostApi, { key: 'key' }),
Effect.provide(Nodemailer.layer(nodemailer)),
Effect.provideService(PublicUrl, new URL(`http://localhost:${port}`)),
Effect.provideService(SessionSecret, Redacted.make('')),
Effect.provideService(FetchHttpClient.Fetch, fetch as unknown as typeof globalThis.fetch),
Effect.provide(LibsqlClient.layer({ url: `file:${testInfo.outputPath('database.db')}` })),
Effect.provide(TemplatePage.optionsLayer({ fathomId: Option.none(), environmentLabel: Option.none() })),
Expand Down
5 changes: 4 additions & 1 deletion src/Context.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Context } from 'effect'
import { Context, type Redacted } from 'effect'
import type { LoggerEnv } from 'logger-fp-ts'
import type { app, ConfigEnv } from './app.js'
import type { EnvVars } from './env.js'
Expand All @@ -25,6 +25,7 @@ export class ExpressConfig extends Context.Tag('ExpressConfig')<
| 'ghostApi'
| 'nodemailer'
| 'publicUrl'
| 'secret'
| 'sleep'
| 'templatePage'
| 'useCrowdinInContext'
Expand All @@ -34,3 +35,5 @@ export class ExpressConfig extends Context.Tag('ExpressConfig')<
export class Locale extends Context.Tag('Locale')<Locale, SupportedLocale>() {}

export class FlashMessage extends Context.Tag('CurrentFlashMessage')<FlashMessage, typeof FlashMessageSchema.Type>() {}

export class SessionSecret extends Context.Tag('SessionSecret')<SessionSecret, Redacted.Redacted>() {}
7 changes: 4 additions & 3 deletions src/ExpressServer.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { FetchHttpClient } from '@effect/platform'
import KeyvRedis from '@keyv/redis'
import { Effect } from 'effect'
import { Effect, Redacted } from 'effect'
import Keyv from 'keyv'
import { app } from './app.js'
import { DeprecatedEnvVars, DeprecatedLoggerEnv, DeprecatedSleepEnv, ExpressConfig } from './Context.js'
import { DeprecatedEnvVars, DeprecatedLoggerEnv, DeprecatedSleepEnv, ExpressConfig, SessionSecret } from './Context.js'
import * as EffectToFpts from './EffectToFpts.js'
import { CanWriteComments, UseCrowdinInContext } from './feature-flags.js'
import { GhostApi } from './ghost.js'
Expand All @@ -25,6 +25,7 @@ export const expressServer = Effect.gen(function* () {
const templatePage = yield* TemplatePage
const useCrowdinInContext = yield* UseCrowdinInContext
const ghostApi = yield* GhostApi
const secret = yield* SessionSecret

return app({
canWriteComments,
Expand All @@ -34,6 +35,7 @@ export const expressServer = Effect.gen(function* () {
ghostApi,
nodemailer,
publicUrl,
secret: Redacted.value(secret),
...sleep,
templatePage,
useCrowdinInContext,
Expand Down Expand Up @@ -95,7 +97,6 @@ export const ExpressConfigLive = Effect.gen(function* () {
researchInterestsStore: new Keyv({ emitErrors: false, namespace: 'research-interests', store: createKeyvStore() }),
reviewRequestStore: new Keyv({ emitErrors: false, namespace: 'review-request', store: createKeyvStore() }),
scietyListToken: env.SCIETY_LIST_TOKEN,
secret: env.SECRET,
sessionCookie: 'session',
sessionStore: new Keyv({
emitErrors: false,
Expand Down
9 changes: 5 additions & 4 deletions src/WebApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import {
Path,
} from '@effect/platform'
import cookieSignature from 'cookie-signature'
import { Cause, Config, Effect, flow, Layer, Option, pipe, Schema } from 'effect'
import { Cause, Config, Effect, flow, Layer, Option, pipe, Redacted, Schema } from 'effect'
import { StatusCodes } from 'http-status-codes'
import { Express, ExpressConfig, FlashMessage, Locale } from './Context.js'
import { Express, ExpressConfig, FlashMessage, Locale, SessionSecret } from './Context.js'
import { ExpressHttpApp } from './ExpressHttpApp.js'
import { expressServer } from './ExpressServer.js'
import { CanChooseLocale, UseCrowdinInContext } from './feature-flags.js'
Expand Down Expand Up @@ -143,13 +143,14 @@ const getFlashMessage = HttpMiddleware.make(app =>

const getLoggedInUser = HttpMiddleware.make(app =>
Effect.gen(function* () {
const { secret, sessionCookie, sessionStore } = yield* ExpressConfig
const secret = yield* SessionSecret
const { sessionCookie, sessionStore } = yield* ExpressConfig

const session = yield* pipe(
HttpServerRequest.schemaCookies(
Schema.Struct({ session: pipe(Schema.propertySignature(Schema.String), Schema.fromKey(sessionCookie)) }),
),
Effect.andThen(({ session }) => cookieSignature.unsign(session, secret)),
Effect.andThen(({ session }) => cookieSignature.unsign(session, Redacted.value(secret))),
Effect.andThen(Schema.decodeUnknown(Uuid.UuidSchema)),
Effect.andThen(sessionId => sessionStore.get(sessionId)),
Effect.andThen(Schema.decodeUnknown(Schema.Struct({ user: UserSchema }))),
Expand Down
1 change: 0 additions & 1 deletion src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ const EnvD = pipe(
ORCID_API_URL: withDefault(UrlD, new URL('https://pub.orcid.org/')),
REMOVED_PREREVIEWS: withDefault(CommaSeparatedListD(IntD), []),
SCIETY_LIST_TOKEN: withDefault(NonEmptyStringC, v4()() as unknown as NonEmptyString),
SECRET: D.string,
SLACK_API_TOKEN: D.string,
SLACK_CLIENT_ID: D.string,
SLACK_CLIENT_SECRET: D.string,
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Config, Effect, Function, Layer, Logger, LogLevel, Schema } from 'effec
import { pipe } from 'fp-ts/lib/function.js'
import { createServer } from 'http'
import fetch from 'make-fetch-happen'
import { DeprecatedEnvVars, DeprecatedLoggerEnv, ExpressConfig } from './Context.js'
import { DeprecatedEnvVars, DeprecatedLoggerEnv, ExpressConfig, SessionSecret } from './Context.js'
import { DeprecatedLogger, makeDeprecatedEnvVars, makeDeprecatedLoggerEnv } from './DeprecatedServices.js'
import { ExpressConfigLive } from './ExpressServer.js'
import * as FeatureFlags from './feature-flags.js'
Expand Down Expand Up @@ -68,6 +68,7 @@ pipe(
}),
),
Effect.provideServiceEffect(PublicUrl, Config.url('PUBLIC_URL')),
Effect.provideServiceEffect(SessionSecret, Config.redacted('SECRET')),
Logger.withMinimumLogLevel(LogLevel.Debug),
Effect.provide(Logger.replaceEffect(Logger.defaultLogger, DeprecatedLogger)),
Effect.provideServiceEffect(DeprecatedLoggerEnv, makeDeprecatedLoggerEnv),
Expand Down

0 comments on commit b392b1f

Please sign in to comment.