-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add redis support to nextjs fullstack example
- Loading branch information
Showing
10 changed files
with
202 additions
and
96 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export interface Datastore { | ||
setUser(name: string, value: string): Promise<void>; | ||
getUser(name: string): Promise<string | null>; | ||
hasUser(name: string): Promise<boolean>; | ||
getLogin(name: string): Promise<string | null>; | ||
setLogin(name: string, value: string): Promise<void>; | ||
hasLogin(name: string): Promise<boolean>; | ||
removeLogin(name: string): Promise<void>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { readFile, writeFile } from "fs/promises"; | ||
import { Datastore } from "./Datastore"; | ||
|
||
type LoginState = { value: string; timestamp: number }; | ||
|
||
export default class InMemoryStore implements Datastore { | ||
constructor( | ||
private users: Record<string, string> = {}, | ||
private logins: Record<string, LoginState> = {}, | ||
private listeners: (() => Promise<void>)[] = [] | ||
) {} | ||
addListener(listener: () => Promise<void>) { | ||
this.listeners.push(listener); | ||
return () => { | ||
const index = this.listeners.indexOf(listener); | ||
if (index !== -1) { | ||
this.listeners.splice(index, 1); | ||
} | ||
}; | ||
} | ||
_notifyListeners() { | ||
return Promise.all(this.listeners.map((f) => f())); | ||
} | ||
static empty() { | ||
return new InMemoryStore({}, {}); | ||
} | ||
stringify() { | ||
return JSON.stringify( | ||
{ | ||
logins: this.logins, | ||
users: this.users, | ||
}, | ||
null, | ||
2 | ||
); | ||
} | ||
async getUser(name: string) { | ||
return this.users[name]; | ||
} | ||
async hasUser(name: string) { | ||
return this.users[name] != null; | ||
} | ||
async getLogin(name: string) { | ||
const hasLogin = await this.hasLogin(name); | ||
return hasLogin ? this.logins[name].value : null; | ||
} | ||
async hasLogin(name: string) { | ||
const login = this.logins[name]; | ||
if (login == null) return false; | ||
const now = new Date().getTime(); | ||
const elapsed = now - login.timestamp; | ||
return elapsed < 2000; | ||
} | ||
async setUser(name: string, value: string) { | ||
this.users[name] = value; | ||
await this._notifyListeners(); | ||
} | ||
async setLogin(name: string, value: string) { | ||
this.logins[name] = { value, timestamp: new Date().getTime() }; | ||
await this._notifyListeners(); | ||
} | ||
async removeLogin(name: string) { | ||
delete this.logins[name]; | ||
await this._notifyListeners(); | ||
} | ||
} | ||
|
||
export async function readDatabaseFile(filePath: string) { | ||
const json = await readFile(filePath, "utf-8"); | ||
const data = JSON.parse(json); | ||
const db = new InMemoryStore(data.serverSetup, data.users, data.logins); | ||
return db; | ||
} | ||
|
||
export function writeDatabaseFile(filePath: string, db: InMemoryStore) { | ||
const data = db.stringify(); | ||
return writeFile(filePath, data); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import * as redis from "redis"; | ||
import { Datastore } from "./Datastore"; | ||
|
||
export default class RedisStore implements Datastore { | ||
private readonly client: ReturnType<typeof redis.createClient>; | ||
constructor(url: string) { | ||
this.client = redis.createClient({ url }); | ||
} | ||
|
||
onError(handler: (err: unknown) => void) { | ||
this.client.on("error", handler); | ||
} | ||
|
||
connect() { | ||
return this.client.connect(); | ||
} | ||
|
||
async getUser(name: string) { | ||
return this.client.get(`user:${name}`); | ||
} | ||
|
||
async hasUser(name: string) { | ||
const user = await this.getUser(name); | ||
return user != null; | ||
} | ||
|
||
getLogin(name: string) { | ||
return this.client.get(`login:${name}`); | ||
} | ||
|
||
async hasLogin(name: string) { | ||
const login = await this.getLogin(name); | ||
return login != null; | ||
} | ||
|
||
async setUser(name: string, value: string) { | ||
await this.client.set(`user:${name}`, value); | ||
} | ||
|
||
async setLogin(name: string, value: string) { | ||
await this.client.set(`login:${name}`, value, { EX: 2 }); | ||
} | ||
|
||
async removeLogin(name: string) { | ||
await this.client.del(`login:${name}`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
export function requireEnv(key: string) { | ||
const value = process.env[key]; | ||
if (value == null) throw new Error(`missing value for env variable "${key}"`); | ||
return value; | ||
} | ||
|
||
export function hasEnv(key: string) { | ||
return process.env[key] != null; | ||
} | ||
|
||
export function getEnv(key: string, defaultValue: string) { | ||
return process.env[key] ?? defaultValue; | ||
} | ||
|
||
export const SERVER_SETUP = requireEnv("OPAQUE_SERVER_SETUP"); | ||
export const ENABLE_REDIS = hasEnv("ENABLE_REDIS"); | ||
export const REDIS_URL = getEnv("REDIS_URL", "redis://127.0.0.1:6379"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.