Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to singleton approach #117

Merged
merged 1 commit into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import Discord from 'next-auth/providers/discord'
import { addUser } from './lib/db/users'
import { AccountType, type User } from './types/User'
import { MongoDBAdapter } from '@auth/mongodb-adapter'
import { dbClient } from './lib/db/db'
import type { NextAuthConfig } from 'next-auth'
import { findOneTyped } from './lib/db/dbTyped'
import { Types } from './types/Components'
import clientPromise from './lib/db/mongoDB'

export const authOptions: NextAuthConfig = {
providers: [Discord],
Expand Down Expand Up @@ -85,7 +85,7 @@ export const authOptions: NextAuthConfig = {
},

// A database is optional, but required to persist accounts in a database
adapter: MongoDBAdapter(dbClient(), {
adapter: MongoDBAdapter(clientPromise, {
collections: {
Users: 'nextauth_users',
Sessions: 'nextauth_sessions',
Expand Down
40 changes: 11 additions & 29 deletions lib/db/db.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// docker run --name index-db -d -p 27017:27017 mongo
import { MongoClient } from 'mongodb'
import { hasOwnProperty } from '../utils'
import { cleanId, polluteId } from './utils'
import clientPromise from './mongoDB'

const uri =
'DATABASE_URL' in process.env
Expand All @@ -11,8 +11,6 @@ if (typeof uri !== 'string') {
throw Error('Unable to connect to DB due to missing DATABASE_URL')
}

export const dbClient = () => new MongoClient(uri, { maxPoolSize: 5 })

export async function exportData(isAdmin = false) {
if (isAdmin) {
return {
Expand All @@ -34,10 +32,8 @@ export async function exportData(isAdmin = false) {
}

export async function getAll(collection: string): Promise<object[]> {
const client = await dbClient().connect()
const db = client.db('index')
const db = (await clientPromise).db('index')
let data = await db.collection(collection).find().toArray()
client.close()
if (data.length > 0 && hasOwnProperty(data[0], 'name')) {
data = data.sort((a, b) => (a.name < b.name ? -1 : 1))
}
Expand All @@ -49,21 +45,18 @@ export async function find(
collection: string,
query: Record<string, any>
): Promise<object[]> {
const client = await dbClient().connect()
const db = client.db('index')
const data = await db.collection(collection).find(polluteId(query)).toArray()
client.close()
return cleanId(data)
const db = (await clientPromise).db('index')
return cleanId(
await db.collection(collection).find(polluteId(query)).toArray()
)
}

export async function findOne(
collection: string,
query: Record<string, any>
): Promise<object | null> {
const client = await dbClient().connect()
const db = client.db('index')
const db = (await clientPromise).db('index')
const found = await db.collection(collection).findOne(polluteId(query))
client.close()
if (found === null) {
return null
}
Expand All @@ -74,10 +67,8 @@ export async function count(
collection: string,
query: Record<string, any> = {}
): Promise<number> {
const client = await dbClient().connect()
const db = client.db('index')
const db = (await clientPromise).db('index')
const data = await db.collection(collection).countDocuments(polluteId(query))
client.close()
return data
}

Expand All @@ -92,15 +83,13 @@ export async function insert(
collection: string,
data: Record<string, any>
): Promise<string> {
const client = await dbClient().connect()
let tries = 1
while (tries < 3) {
try {
const db = client.db('index')
const db = (await clientPromise).db('index')
data.createdAt = new Date()
data.lastModified = new Date()
const { insertedId } = await db.collection(collection).insertOne(data)
client.close()
return insertedId.toString()
} catch (error) {
console.error(
Expand All @@ -112,7 +101,6 @@ export async function insert(
tries++
}
}
client.close()
throw Error('Unable to insert entry into ' + collection + ' after 3 retires')
}

Expand All @@ -121,16 +109,14 @@ export async function updateOne(
query: Record<string, any>,
data: Record<string, any>
) {
const client = await dbClient().connect()
let tries = 1
while (tries < 3) {
try {
const db = client.db('index')
const db = (await clientPromise).db('index')
await db.collection(collection).updateOne(polluteId(query), {
$set: data,
$currentDate: { lastModified: true },
})
client.close()
return
} catch (error) {
console.error(
Expand All @@ -139,21 +125,18 @@ export async function updateOne(
tries++
}
}
client.close()
throw Error('Unable to update entry of ' + collection + ' after 3 retires')
}

export async function deleteOne(
collection: string,
query: Record<string, any>
) {
const client = await dbClient().connect()
let tries = 1
while (tries < 3) {
try {
const db = client.db('index')
const db = (await clientPromise).db('index')
await db.collection(collection).deleteOne(polluteId(query))
client.close()
return
} catch (error) {
console.error(
Expand All @@ -165,6 +148,5 @@ export async function deleteOne(
tries++
}
}
client.close()
throw Error('Unable to delete entry from ' + collection + ' after 3 retires')
}
34 changes: 10 additions & 24 deletions lib/db/itemScreenshots.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { dbClient } from './db'
import { GridFSBucket } from 'mongodb'
import { Readable } from 'stream'
import clientPromise from './mongoDB'

export function bufferToStream(buffer: Buffer) {
let stream = new Readable()
Expand Down Expand Up @@ -32,8 +32,7 @@ export async function addItemScreenshot(img: Uint8Array, itemId: string) {
imgStream.push(img)
imgStream.push(null)

const client = await dbClient().connect()
const db = client.db('index')
const db = (await clientPromise).db('index')
const bucket = new GridFSBucket(db, {
bucketName: 'itemScreenshots',
})
Expand All @@ -57,51 +56,38 @@ export async function addItemScreenshot(img: Uint8Array, itemId: string) {
stream.on('finish', resolve)
imgStream.on('error', reject)
})
client.close()
}

export async function getItemScreenshotBuffer(itemId: string) {
const client = await dbClient().connect()
const db = client.db('index')
const db = (await clientPromise).db('index')
const bucket = new GridFSBucket(db, {
bucketName: 'itemScreenshots',
})

const data = await streamToBuffer(bucket.openDownloadStreamByName(itemId))
client.close()
return data
return await streamToBuffer(bucket.openDownloadStreamByName(itemId))
}

export async function screenshotExists(itemId: string) {
const client = await dbClient().connect()
const db = client.db('index')
const db = (await clientPromise).db('index')
const bucket = new GridFSBucket(db, {
bucketName: 'itemScreenshots',
})
const cursor = await bucket.find({ filename: itemId })
const data = await cursor.hasNext()
client.close()
return data
return await cursor.hasNext()
}

export async function clearAllScreenshots() {
const client = await dbClient().connect()
const db = client.db('index')
const db = (await clientPromise).db('index')
const bucket = new GridFSBucket(db, {
bucketName: 'itemScreenshots',
})
const data = await bucket.drop()
client.close()
return data
return await bucket.drop()
}

export async function listScreenshotsOfItem(itemId: string) {
const client = await dbClient().connect()
const db = client.db('index')
const db = (await clientPromise).db('index')
const bucket = new GridFSBucket(db, {
bucketName: 'itemScreenshots',
})
const data = bucket.find({ filename: itemId })
client.close()
return data
return bucket.find({ filename: itemId })
}
44 changes: 44 additions & 0 deletions lib/db/mongoDB.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { MongoClient } from 'mongodb'

const uri = (
'DATABASE_URL' in process.env
? process.env.DATABASE_URL
: 'mongodb://localhost'
) as string
if (typeof uri !== 'string') {
throw Error('Unable to connect to DB due to missing DATABASE_URL')
}

declare global {
var _mongoClientPromise: Promise<MongoClient>
}

class Singleton {
private static _instance: Singleton
private client: MongoClient
private clientPromise: Promise<MongoClient>
private constructor() {
this.client = new MongoClient(uri, {
maxPoolSize: 5,
directConnection: true,
})
this.clientPromise = this.client.connect()
if (process.env.NODE_ENV === 'development') {
// In development mode, use a global variable so that the value
// is preserved across module reloads caused by HMR (Hot Module Replacement).
global._mongoClientPromise = this.clientPromise
}
}

public static get instance() {
if (!this._instance) {
this._instance = new Singleton()
}
return this._instance.clientPromise
}
}
const clientPromise = Singleton.instance

// Export a module-scoped MongoClient promise. By doing this in a
// separate module, the client can be shared across functions.
export default clientPromise
7 changes: 3 additions & 4 deletions lib/db/views.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { count, dbClient, deleteOne, find, findOne, getAll, insert } from './db'
import { count, deleteOne, find, findOne, getAll, insert } from './db'
import { cleanId } from './utils'
import { Types } from '../../types/Components'
import { findOneTyped } from './dbTyped'
import clientPromise from './mongoDB'

export async function getViews() {
return await getAll('views')
}

export async function getLastViews(type: Types, n: number) {
const client = await dbClient().connect()
const db = client.db('index')
const db = (await clientPromise).db('index')
const data = cleanId(
await db
.collection('views')
Expand All @@ -18,7 +18,6 @@ export async function getLastViews(type: Types, n: number) {
.limit(n)
.toArray()
)
client.close()
console.log('Found', data.length, 'entries in views table for', type)

// count what has been popular recently
Expand Down
Loading