stacker.news/pages/api/graphql.js
SatsAllDay 15f9950477
Store hashed and salted email addresses (#1111)
* first pass of hashing user emails

* use salt

* add a salt to .env.development (prod salt needs to be kept a secret)
* move `hashEmail` util to a new util module

* trigger a one-time job to migrate existing emails via the worker

so we can use the salt from an env var

* move newsletter signup

move newsletter signup to prisma adapter create user with email code path
so we can still auto-enroll email accounts without having to persist the email address
in plaintext

* remove `email` from api key session lookup query

* drop user email index before dropping column

* restore email column, just null values instead

* fix function name

* fix salt and hash raw sql statement

* update auth methods email type in typedefs from str to bool

* remove todo comment

* lowercase email before hashing during migration

* check for emailHash and email to accommodate migration window

update our lookups to check for a matching emailHash, and then a matching
email, in that order, to accommodate the case that a user tries to login
via email while the migration is running, and their account has not yet been migrated

also update sndev to have a command `./sndev email` to launch the mailhog inbox in your browser

also update `./sndev login` to hash the generated email address and insert it into the db record

* update sndev help

* update awards.csv

* update the hack in next-auth to re-use the email supplied on input to `getUserByEmail`

* consolidate console.error logs

* create generic open command

---------

Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com>
Co-authored-by: keyan <keyan.kousha+huumn@gmail.com>
2024-05-04 18:06:15 -05:00

82 lines
2.6 KiB
JavaScript

import { ApolloServer } from '@apollo/server'
import { startServerAndCreateNextHandler } from '@as-integrations/next'
import resolvers from '@/api/resolvers'
import models from '@/api/models'
import lnd from '@/api/lnd'
import typeDefs from '@/api/typeDefs'
import { getServerSession } from 'next-auth/next'
import { getAuthOptions } from './auth/[...nextauth]'
import search from '@/api/search'
import {
ApolloServerPluginLandingPageLocalDefault,
ApolloServerPluginLandingPageProductionDefault
} from '@apollo/server/plugin/landingPage/default'
const apolloServer = new ApolloServer({
typeDefs,
resolvers,
introspection: true,
plugins: [{
requestDidStart (initialRequestContext) {
return {
executionDidStart () {
return {
willResolveField ({ source, args, context, info }) {
const start = process.hrtime.bigint()
return (error, result) => {
const end = process.hrtime.bigint()
const ms = (end - start) / 1000000n
if (ms > 50) {
console.log(`Field ${info.parentType.name}.${info.fieldName} took ${ms}ms`)
}
if (error) {
console.log(`Field ${info.parentType.name}.${info.fieldName} failed with ${error}`)
}
}
},
async executionDidEnd (err) {
if (err) {
console.error('hey bud', err)
}
}
}
}
}
}
},
process.env.NODE_ENV === 'production'
? ApolloServerPluginLandingPageProductionDefault(
{ embed: { endpointIsEditable: false, persistExplorerState: true, displayOptions: { theme: 'dark' } }, footer: false })
: ApolloServerPluginLandingPageLocalDefault(
{ embed: { endpointIsEditable: false, persistExplorerState: true, displayOptions: { theme: 'dark' } }, footer: false })]
})
export default startServerAndCreateNextHandler(apolloServer, {
context: async (req, res) => {
const apiKey = req.headers['x-api-key']
let session
if (apiKey) {
const [user] = await models.$queryRaw`
SELECT id, name, "apiKeyEnabled"
FROM users
WHERE "apiKeyHash" = encode(digest(${apiKey}, 'sha256'), 'hex')
LIMIT 1`
if (user?.apiKeyEnabled) {
const { apiKeyEnabled, ...sessionFields } = user
session = { user: { ...sessionFields, apiKey: true } }
}
} else {
session = await getServerSession(req, res, getAuthOptions(req))
}
return {
models,
headers: req.headers,
lnd,
me: session
? session.user
: null,
search
}
}
})