Skip to content
Draft
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
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
16.14.2
16.18.0
276 changes: 149 additions & 127 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"initMigration": "npm run typeorm -- migration:generate -t 1642180264563 -d ormconfig.js \"src/Common/Migrations/Database/init\""
},
"engines": {
"node": ">=16"
"node": ">=16.18.0"
},
"keywords": [],
"author": "",
Expand All @@ -49,7 +49,7 @@
"autolinker": "^3.14.3",
"body-parser": "^1.19.0",
"cache-manager": "^3.4.4",
"cache-manager-redis-store": "^2.0.0",
"cache-manager-redis-store": "^3.0.1",
"commander": "^8.0.0",
"comment-json": "^4.1.1",
"connect-typeorm": "^2.0.0",
Expand Down Expand Up @@ -115,7 +115,6 @@
"@tsconfig/node14": "^1.0.0",
"@types/async": "^3.2.7",
"@types/cache-manager": "^3.4.2",
"@types/cache-manager-redis-store": "^2.0.0",
"@types/chai": "^4.3.0",
"@types/chai-as-promised": "^7.1.5",
"@types/cookie-parser": "^1.4.2",
Expand Down
47 changes: 47 additions & 0 deletions src/Common/Cache/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {CacheOptions} from "../interfaces";
import cacheManager, {Cache} from "cache-manager";
import {redisStore} from "cache-manager-redis-store";
import {create as createMemoryStore} from "../../Utils/memoryStore";
import {CacheProvider} from "../Infrastructure/Atomic";
import {cacheOptDefaults} from "../defaults";

export const buildCacheOptionsFromProvider = (provider: CacheProvider | any): CacheOptions => {
if (typeof provider === 'string') {
return {
store: provider as CacheProvider,
...cacheOptDefaults
}
}
return {
store: 'memory',
...cacheOptDefaults,
...provider,
}
}
export const createCacheManager = async (options: CacheOptions): Promise<Cache> => {
const {store, max, ttl = 60, host = 'localhost', port, auth_pass, db, ...rest} = options;
switch (store) {
case 'none':
return cacheManager.caching({store: 'none', max, ttl});
case 'redis':
const rStore = await redisStore(
{
socket: {
host,
port
},
password: auth_pass,
database: db,
}
);
return cacheManager.caching({
store: rStore,
ttl,
...rest,
});
case 'memory':
default:
//return cacheManager.caching({store: 'memory', max, ttl});
return cacheManager.caching({store: {create: createMemoryStore}, max, ttl, shouldCloneBeforeSet: false});
}
}
6 changes: 6 additions & 0 deletions src/Common/Typings/support.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,9 @@ declare module 'wink-sentiment' {

export default sentiment;
}

declare module 'cache-manager-redis-store' {
import {RedisClientOptions} from "@redis/client";
import {Cache, CachingConfig} from "cache-manager";
export async function redisStore(config: RedisClientOptions & Partial<CachingConfig>): Cache;
}
23 changes: 11 additions & 12 deletions src/Subreddit/SubredditResources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@ import {
asActivity,
asSubmission,
asUserNoteCriteria,
buildCacheOptionsFromProvider,
buildCachePrefix,
cacheStats,
createCacheManager,
escapeRegex,
FAIL,
fetchExternalResult,
Expand Down Expand Up @@ -173,6 +171,7 @@ import ConfigParseError from "../Utils/ConfigParseError";
import {ActivityReport} from "../Common/Entities/ActivityReport";
import {ActionResultEntity} from "../Common/Entities/ActionResultEntity";
import {ActivitySource} from "../Common/ActivitySource";
import {buildCacheOptionsFromProvider, createCacheManager} from "../Common/Cache";

export const DEFAULT_FOOTER = '\r\n*****\r\nThis action was performed by [a bot.]({{botLink}}) Mention a moderator or [send a modmail]({{modmailLink}}) if you have any ideas, questions, or concerns about this action.';

Expand Down Expand Up @@ -3545,7 +3544,7 @@ export class BotResourcesManager {
authorTTL: number = 10000;
enabled: boolean = true;
modStreams: Map<string, SPoll<Snoowrap.Submission | Snoowrap.Comment>> = new Map();
defaultCache: Cache;
defaultCache: Promise<Cache>;
defaultCacheConfig: StrongCache
defaultCacheMigrated: boolean = false;
cacheType: string = 'none';
Expand Down Expand Up @@ -3649,7 +3648,7 @@ export class BotResourcesManager {
// });

let opts: SubredditResourceOptions = {
cache: this.defaultCache,
cache: await this.defaultCache,
cacheType: this.cacheType,
cacheSettingsHash: hash,
ttl: this.ttlDefaults,
Expand Down Expand Up @@ -3680,7 +3679,7 @@ export class BotResourcesManager {
trueProvider.prefix = subPrefix;
const eventsMax = this.actionedEventsMaxDefault !== undefined ? Math.min(actionedEventsMax, this.actionedEventsMaxDefault) : actionedEventsMax;
opts = {
cache: createCacheManager(trueProvider),
cache: await createCacheManager(trueProvider),
actionedEventsMax: eventsMax,
cacheType: trueProvider.store,
cacheSettingsHash: hash,
Expand All @@ -3695,7 +3694,7 @@ export class BotResourcesManager {
await runMigrations(opts.cache, opts.logger, trueProvider.prefix);
}
} else if(!this.defaultCacheMigrated) {
await runMigrations(this.defaultCache, this.logger, opts.prefix);
await runMigrations(await this.defaultCache, this.logger, opts.prefix);
this.defaultCacheMigrated = true;
}

Expand Down Expand Up @@ -3730,7 +3729,7 @@ export class BotResourcesManager {
}

async getPendingSubredditInvites(): Promise<(string[])> {
const subredditNames = await this.defaultCache.get(`modInvites`);
const subredditNames = await (await this.defaultCache).get(`modInvites`);
if (subredditNames !== undefined && subredditNames !== null) {
return subredditNames as string[];
}
Expand All @@ -3741,7 +3740,7 @@ export class BotResourcesManager {
if(subreddit === null || subreddit === undefined || subreddit == '') {
throw new CMError('Subreddit name cannot be empty');
}
let subredditNames = await this.defaultCache.get(`modInvites`) as (string[] | undefined | null);
let subredditNames = await (await this.defaultCache).get(`modInvites`) as (string[] | undefined | null);
if (subredditNames === undefined || subredditNames === null) {
subredditNames = [];
}
Expand All @@ -3751,22 +3750,22 @@ export class BotResourcesManager {
throw new CMError(`An invite for the Subreddit '${subreddit}' already exists`);
}
subredditNames.push(cleanName);
await this.defaultCache.set(`modInvites`, subredditNames, {ttl: 0});
await (await this.defaultCache).set(`modInvites`, subredditNames, {ttl: 0});
return;
}

async deletePendingSubredditInvite(subreddit: string): Promise<void> {
let subredditNames = await this.defaultCache.get(`modInvites`) as (string[] | undefined | null);
let subredditNames = await (await this.defaultCache).get(`modInvites`) as (string[] | undefined | null);
if (subredditNames === undefined || subredditNames === null) {
subredditNames = [];
}
subredditNames = subredditNames.filter(x => x.toLowerCase() !== subreddit.trim().toLowerCase());
await this.defaultCache.set(`modInvites`, subredditNames, {ttl: 0});
await (await this.defaultCache).set(`modInvites`, subredditNames, {ttl: 0});
return;
}

async clearPendingSubredditInvites(): Promise<void> {
await this.defaultCache.del(`modInvites`);
await (await this.defaultCache).del(`modInvites`);
return;
}
}
Expand Down
21 changes: 11 additions & 10 deletions src/Web/Client/StorageProvider.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {SessionOptions, Store} from "express-session";
import {TypeormStore} from "connect-typeorm";
import {InviteData} from "../Common/interfaces";
import {buildCachePrefix, createCacheManager, mergeArr} from "../../util";
import {buildCachePrefix, mergeArr} from "../../util";
import {Cache} from "cache-manager";
// @ts-ignore
import CacheManagerStore from 'express-session-cache-manager'
Expand All @@ -11,6 +11,7 @@ import {ClientSession} from "../../Common/WebEntities/ClientSession";
import {Logger} from "winston";
import {WebSetting} from "../../Common/WebEntities/WebSetting";
import {ErrorWithCause} from "pony-cause";
import {createCacheManager} from "../../Common/Cache";

export interface CacheManagerStoreOptions {
prefix?: string
Expand All @@ -24,7 +25,7 @@ export type TypeormStoreOptions = Partial<SessionOptions & {
}>;

interface IWebStorageProvider {
createSessionStore(options?: CacheManagerStoreOptions | TypeormStoreOptions): Store
createSessionStore(options?: CacheManagerStoreOptions | TypeormStoreOptions): Promise<Store>

getSessionSecret(): Promise<string | undefined>

Expand All @@ -48,7 +49,7 @@ abstract class StorageProvider implements IWebStorageProvider {
this.logger = logger.child({labels: ['Web', 'Storage', ...loggerLabels]}, mergeArr);
}

abstract createSessionStore(options?: CacheManagerStoreOptions | TypeormStoreOptions): Store;
abstract createSessionStore(options?: CacheManagerStoreOptions | TypeormStoreOptions): Promise<Store>;

abstract getSessionSecret(): Promise<string | undefined>;

Expand All @@ -57,32 +58,32 @@ abstract class StorageProvider implements IWebStorageProvider {

export class CacheStorageProvider extends StorageProvider {

protected cache: Cache;
protected cache: Promise<Cache>;

constructor(caching: CacheOptions & StorageProviderOptions) {
super(caching);
const {logger, invitesMaxAge, loggerLabels, ...restCache } = caching;
this.cache = createCacheManager({...restCache, prefix: buildCachePrefix(['web'])}) as Cache;
this.cache = createCacheManager({...restCache, prefix: buildCachePrefix(['web'])}) as Promise<Cache>;
this.logger.debug('Using CACHE');
if (caching.store === 'none') {
this.logger.warn(`Using 'none' as cache provider means no one will be able to access the interface since sessions will never be persisted!`);
}
}

createSessionStore(options?: CacheManagerStoreOptions): Store {
return new CacheManagerStore(this.cache, {prefix: 'sess:'});
async createSessionStore(options?: CacheManagerStoreOptions): Promise<Store> {
return new CacheManagerStore((await this.cache), {prefix: 'sess:'});
}

async getSessionSecret() {
const val = await this.cache.get(`sessionSecret`);
const val = await (await this.cache).get(`sessionSecret`);
if (val === null || val === undefined) {
return undefined;
}
return val as string;
}

async setSessionSecret(secret: string) {
await this.cache.set('sessionSecret', secret, {ttl: 0});
await (await this.cache).set('sessionSecret', secret, {ttl: 0});
}

}
Expand All @@ -101,7 +102,7 @@ export class DatabaseStorageProvider extends StorageProvider {
this.logger.debug('Using DATABASE');
}

createSessionStore(options?: TypeormStoreOptions): Store {
async createSessionStore(options?: TypeormStoreOptions): Promise<Store> {
return new TypeormStore(options).connect(this.clientSessionRepo)
}

Expand Down
5 changes: 3 additions & 2 deletions src/Web/Client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
} from "../../Common/interfaces";
import {
buildCachePrefix,
createCacheManager, defaultFormat, filterLogBySubreddit, filterCriteriaSummary, formatFilterData,
defaultFormat, filterLogBySubreddit, filterCriteriaSummary, formatFilterData,
formatLogLineToHtml, filterLogs, getUserAgent,
intersect, isLogLineMinLevel,
LogEntry, parseInstanceLogInfoName, parseInstanceLogName, parseRedditEntity,
Expand Down Expand Up @@ -64,6 +64,7 @@ import {
InviteData, SubredditInviteDataPersisted
} from "../Common/interfaces";
import {open} from "fs/promises";
import {createCacheManager} from "../../Common/Cache";

const emitter = new EventEmitter();

Expand Down Expand Up @@ -323,7 +324,7 @@ const webClient = async (options: OperatorConfigWithFileContext) => {
cookie: {
maxAge: sessionMaxAge * 1000,
},
store: sessionStoreProvider.createSessionStore(sessionStorage === 'database' ? {
store: await sessionStoreProvider.createSessionStore(sessionStorage === 'database' ? {
cleanupLimit: 2,
ttl: sessionMaxAge
} : {}),
Expand Down
53 changes: 7 additions & 46 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
ActionResult,
ActivityDispatch,
ActivityDispatchConfig,
CacheOptions,
CheckSummary,
ImageComparisonResult,
ItemCritPropHelper,
Expand All @@ -35,11 +34,9 @@ import {
} from "./Common/interfaces";
import InvalidRegexError from "./Utils/InvalidRegexError";
import {accessSync, constants, promises} from "fs";
import {cacheOptDefaults, VERSION} from "./Common/defaults";
import cacheManager, {Cache} from "cache-manager";
import redisStore from "cache-manager-redis-store";
import {VERSION} from "./Common/defaults";
import cacheManager from "cache-manager";
import Autolinker from 'autolinker';
import {create as createMemoryStore} from './Utils/memoryStore';
import {LEVEL, MESSAGE} from "triple-beam";
import {Comment, PrivateMessage, RedditUser, Submission, Subreddit} from "snoowrap/dist/objects";
import reRegExp from '@stdlib/regexp-regexp';
Expand Down Expand Up @@ -71,23 +68,23 @@ import {
UserNoteCriteria
} from "./Common/Infrastructure/Filters/FilterCriteria";
import {
ActivitySourceValue,
ActivitySourceData,
ActivitySourceTypes,
CacheProvider,
ActivitySourceValue,
ConfigFormat,
DurationVal,
ExternalUrlContext,
ImageHashCacheData,
ModUserNoteLabel,
modUserNoteLabels,
RedditEntity,
RedditEntityType, RelativeDateTimeMatch,
RedditEntityType,
RelativeDateTimeMatch,
statFrequencies,
StatisticFrequency,
StatisticFrequencyOption,
UrlContext,
WikiContext,
ActivitySourceData
WikiContext
} from "./Common/Infrastructure/Atomic";
import {
AuthorOptions,
Expand Down Expand Up @@ -1765,42 +1762,6 @@ export const cacheStats = (): ResourceStats => {
};
}

export const buildCacheOptionsFromProvider = (provider: CacheProvider | any): CacheOptions => {
if(typeof provider === 'string') {
return {
store: provider as CacheProvider,
...cacheOptDefaults
}
}
return {
store: 'memory',
...cacheOptDefaults,
...provider,
}
}

export const createCacheManager = (options: CacheOptions): Cache => {
const {store, max, ttl = 60, host = 'localhost', port, auth_pass, db, ...rest} = options;
switch (store) {
case 'none':
return cacheManager.caching({store: 'none', max, ttl});
case 'redis':
return cacheManager.caching({
store: redisStore,
host,
port,
auth_pass,
db,
ttl,
...rest,
});
case 'memory':
default:
//return cacheManager.caching({store: 'memory', max, ttl});
return cacheManager.caching({store: {create: createMemoryStore}, max, ttl, shouldCloneBeforeSet: false});
}
}

export const randomId = () => crypto.randomBytes(20).toString('hex');

export const intersect = (a: Array<any>, b: Array<any>) => {
Expand Down