···2233import { atprotoLoopbackClientMetadata, Keyset, NodeOAuthClient } from '@atproto/oauth-client-node';
44import { JoseKey } from '@atproto/jwk-jose';
55-import { db } from '$lib/server/db';
65import { SessionStore, StateStore } from '$lib/server/atproto/storage';
76import { env } from '$env/dynamic/private';
87import type { OAuthClientMetadataInput } from '@atproto/oauth-types';
88+import { getAValKeyClient } from '$lib/server/cache';
99+9101011//You will need to change these if you are using another collection, can also change by setting the env OAUTH_SCOPES
1112//For permission to all you can uncomment below
···2829export const atpOAuthClient = async () => {
2930 if (!client) {
3031 client = (async () => {
3232+3333+ const valKeyClient = await getAValKeyClient();
3434+3135 const rootDomain = env.OAUTH_DOMAIN ?? '127.0.0.1:5173';
3236 const dev = env.DEV !== undefined;
3337 const isConfidential = env.OAUTH_JWK !== undefined;
···6771 };
68726973 return new NodeOAuthClient({
7070- stateStore: new StateStore(db),
7171- sessionStore: new SessionStore(db),
7474+ stateStore: new StateStore(valKeyClient),
7575+ sessionStore: new SessionStore(valKeyClient),
7276 keyset,
7377 clientMetadata,
7478 // Not needed since this all runs locally to one machine I believe. But if you do run multiple instances and change out the DB from sqlite may need this
+6-5
src/lib/server/atproto/storage.ts
···77 NodeSavedStateStore
88} from '@atproto/oauth-client-node';
99import { Cache, SESSION_STORE, STATE_STORE } from '$lib/server/cache';
1010-import { db } from '$lib/server/db';
1010+import { GlideClient } from '@valkey/valkey-glide';
1111+11121213export class StateStore implements NodeSavedStateStore{
13141415 cache: Cache;
15161616- constructor(database: typeof db) {
1717- this.cache = new Cache(database, STATE_STORE);
1717+ constructor(valKeyClient: GlideClient) {
1818+ this.cache = new Cache(valKeyClient, STATE_STORE, 1_800);
1819 }
19202021 async del(key: string) {
···36373738 cache: Cache;
38393939- constructor(database: typeof db) {
4040- this.cache = new Cache(database, SESSION_STORE);
4040+ constructor(valKeyClient: GlideClient) {
4141+ this.cache = new Cache(valKeyClient, SESSION_STORE);
4142 }
42434344 async del(key: string) {
+61-26
src/lib/server/cache.ts
···11-// A key value key to the database that is mostly used for atproto session storage and state storage during the oauth session creation
22-// The "stores" are divided up by a where on the store type so it can share the same interface just with that
11+import { logger } from './logger';
22+import { env } from '$env/dynamic/private';
33+import { GlideClient, TimeUnit } from '@valkey/valkey-glide';
3444-import { db } from './db';
55-import { keyValueStore } from './db/schema';
66-import { and, eq } from 'drizzle-orm';
55+export const SESSION_STORE = 'atp_sessions:';
66+export const STATE_STORE = 'atp_states:';
7788-export const SESSION_STORE = 'sessions';
99-export const STATE_STORE = 'states';
88+99+let valKeyClient: Promise<GlideClient> | null = null;
1010+1111+1212+export const getAValKeyClient = async () => {
1313+ if (!valKeyClient) {
1414+ valKeyClient = (async () => {
1515+ logger.info('Creating valkey client');
1616+1717+ const addresses = [
1818+ {
1919+ host: env.REDIS_HOST ?? 'localhost',
2020+ // @ts-expect-error Going to leave it to the redis client to throw a run time error for this since
2121+ // it is a run time error to not be able to have redis to connect
2222+ port: env.REDIS_PORT as number ?? 6379
2323+ },
2424+ ];
2525+2626+ return await GlideClient.createClient({
2727+ addresses,
2828+ credentials: env.REDIS_PASSWORD ? { password: env.REDIS_PASSWORD } : undefined,
2929+ useTLS: env.REDIS_TLS === 'true',
3030+ //This may be a bit extreme, will see
3131+ requestTimeout: 500, // 500ms timeout
3232+ });
3333+ })();
3434+ }
3535+ return valKeyClient;
3636+};
3737+10381139export class Cache {
12401313- db: typeof db;
1414- cacheName: string;
4141+ valKeyClient: GlideClient;
4242+ //Set if the cache set should have an expiration
4343+ expire: number | undefined;
4444+ cachePrefix: string;
15451616- constructor(database: typeof db, cacheName: string) {
1717- this.db = database;
1818- this.cacheName = cacheName;
4646+4747+ constructor(glideClient: GlideClient, cachePrefix: string, expire: number | undefined = undefined) {
4848+ this.valKeyClient = glideClient;
4949+ this.expire = expire;
5050+ this.cachePrefix = cachePrefix;
5151+ }
5252+5353+ $key(key: string) {
5454+ return `${this.cachePrefix}${key}`;
1955 }
20562157 async get(key: string) {
2222- const result = await this.db.select().from(keyValueStore).where(and(
2323- eq(keyValueStore.key, key),
2424- eq(keyValueStore.storeName, this.cacheName)
2525- )).limit(1);
2626- if(result.length > 0){
2727- return result[0].value;
5858+ const result = await this.valKeyClient.get(this.$key(key));
5959+ if(result){
6060+ return result.toString();
2861 }
2929- return null;
6262+ return undefined;
3063 }
31643265 async set(key: string, value: string) {
3333- return this.db.insert(keyValueStore)
3434- .values({ key, value, storeName: this.cacheName, createdAt: new Date() })
3535- .onConflictDoUpdate({
3636- target: keyValueStore.key,
3737- set: { value, createdAt: new Date() }
3838- });
6666+ const expiryOptions = this.expire ?
6767+ {
6868+ expiry: {
6969+ type: TimeUnit.Seconds,
7070+ count: this.expire as number
7171+ }
7272+ } : undefined;
7373+ return await this.valKeyClient.set(this.$key(key), value, expiryOptions);
3974 }
40754176 async delete(key: string) {
4242- return this.db.delete(keyValueStore).where(eq(keyValueStore.key, key));
7777+ await this.valKeyClient.del([this.$key(key)]);
4378 }
44794580}