diff --git a/src/routes/getSkipSegments.ts b/src/routes/getSkipSegments.ts index 88e61cb..5252fce 100644 --- a/src/routes/getSkipSegments.ts +++ b/src/routes/getSkipSegments.ts @@ -1,7 +1,8 @@ import { Request, Response } from 'express'; import { config } from '../config'; import { db, privateDB } from '../databases/databases'; -import { Category, DBSegment, OverlappingSegmentGroup, Segment, SegmentCache, VideoData, VideoID, VideoIDHash, Visibility, VotableObject } from "../types/segments.model"; +import { SBRecord } from '../types/lib.model'; +import { Category, DBSegment, HashedIP, IPAddress, OverlappingSegmentGroup, Segment, SegmentCache, VideoData, VideoID, VideoIDHash, Visibility, VotableObject } from "../types/segments.model"; import { getHash } from '../utils/getHash'; import { getIP } from '../utils/getIP'; import { Logger } from '../utils/logger'; @@ -20,14 +21,14 @@ function prepareCategorySegments(req: Request, videoID: VideoID, category: Categ } if (cache.shadowHiddenSegmentIPs[videoID] === undefined) { - cache.shadowHiddenSegmentIPs[videoID] = privateDB.prepare('all', 'SELECT hashedIP FROM sponsorTimes WHERE videoID = ?', [videoID]); + cache.shadowHiddenSegmentIPs[videoID] = privateDB.prepare('all', 'SELECT hashedIP FROM sponsorTimes WHERE videoID = ?', [videoID]) as { hashedIP: HashedIP }[]; } //if this isn't their ip, don't send it to them return cache.shadowHiddenSegmentIPs[videoID].some((shadowHiddenSegment) => { if (cache.userHashedIP === undefined) { //hash the IP only if it's strictly necessary - cache.userHashedIP = getHash(getIP(req) + config.globalSalt); + cache.userHashedIP = getHash((getIP(req) + config.globalSalt) as IPAddress); } return shadowHiddenSegment.hashedIP === cache.userHashedIP; @@ -46,12 +47,12 @@ function getSegmentsByVideoID(req: Request, videoID: string, categories: Categor const segments: Segment[] = []; try { - const segmentsByCategory: Record = db + const segmentsByCategory: SBRecord = db .prepare( 'all', `SELECT startTime, endTime, votes, UUID, category, shadowHidden FROM sponsorTimes WHERE videoID = ? AND category IN (${Array(categories.length).fill('?').join()}) ORDER BY startTime`, [videoID, categories] - ).reduce((acc: Record, segment: DBSegment) => { + ).reduce((acc: SBRecord, segment: DBSegment) => { acc[segment.category] = acc[segment.category] || []; acc[segment.category].push(segment); @@ -59,7 +60,7 @@ function getSegmentsByVideoID(req: Request, videoID: string, categories: Categor }, {}); for (const [category, categorySegments] of Object.entries(segmentsByCategory)) { - segments.push(...prepareCategorySegments(req, videoID, category, categorySegments, cache)); + segments.push(...prepareCategorySegments(req, videoID as VideoID, category as Category, categorySegments, cache)); } return segments; @@ -71,12 +72,12 @@ function getSegmentsByVideoID(req: Request, videoID: string, categories: Categor } } -function getSegmentsByHash(req: Request, hashedVideoIDPrefix: VideoIDHash, categories: Category[]): Record { +function getSegmentsByHash(req: Request, hashedVideoIDPrefix: VideoIDHash, categories: Category[]): SBRecord { const cache: SegmentCache = {shadowHiddenSegmentIPs: {}}; - const segments: Record = {}; + const segments: SBRecord = {}; try { - type SegmentWithHashPerVideoID = Record}>; + type SegmentWithHashPerVideoID = SBRecord}>; const segmentPerVideoID: SegmentWithHashPerVideoID = db .prepare( @@ -103,7 +104,7 @@ function getSegmentsByHash(req: Request, hashedVideoIDPrefix: VideoIDHash, categ }; for (const [category, segmentPerCategory] of Object.entries(videoData.segmentPerCategory)) { - segments[videoID].segments.push(...prepareCategorySegments(req, videoID, category, segmentPerCategory, cache)); + segments[videoID].segments.push(...prepareCategorySegments(req, videoID as VideoID, category as Category, segmentPerCategory, cache)); } } diff --git a/src/routes/getSkipSegmentsByHash.ts b/src/routes/getSkipSegmentsByHash.ts index 9273465..5c557ac 100644 --- a/src/routes/getSkipSegmentsByHash.ts +++ b/src/routes/getSkipSegmentsByHash.ts @@ -4,7 +4,7 @@ import {Request, Response} from 'express'; import { Category, VideoIDHash } from '../types/segments.model'; export async function getSkipSegmentsByHash(req: Request, res: Response) { - let hashPrefix: VideoIDHash = req.params.prefix; + let hashPrefix = req.params.prefix as VideoIDHash; if (!hashPrefixTester(req.params.prefix)) { res.status(400).send("Hash prefix does not match format requirements."); // Exit early on faulty prefix return; diff --git a/src/types/lib.model.ts b/src/types/lib.model.ts new file mode 100644 index 0000000..b58087f --- /dev/null +++ b/src/types/lib.model.ts @@ -0,0 +1,7 @@ +/** + * Better ecord that will work with branded types + * Keys still don't work properly though and are always string + */ +export type SBRecord = { + [P in string | K]: T; +}; \ No newline at end of file diff --git a/src/types/segments.model.ts b/src/types/segments.model.ts index fab891f..47a3ad0 100644 --- a/src/types/segments.model.ts +++ b/src/types/segments.model.ts @@ -1,8 +1,12 @@ -export type SegmentUUID = string; -export type VideoID = string; -export type Category = string; -export type VideoIDHash = string; -export type IPHash = string; +import { HashedValue } from "./hash.model"; +import { SBRecord } from "./lib.model"; + +export type SegmentUUID = string & { __segmentUUIDBrand: unknown }; +export type VideoID = string & { __videoIDBrand: unknown }; +export type Category = string & { __categoryBrand: unknown }; +export type VideoIDHash = VideoID & HashedValue; +export type IPAddress = string & { __ipAddressBrand: unknown }; +export type HashedIP = IPAddress & HashedValue; export interface Segment { category: Category; @@ -45,6 +49,6 @@ export interface VideoData { } export interface SegmentCache { - shadowHiddenSegmentIPs: Record, - userHashedIP?: IPHash + shadowHiddenSegmentIPs: SBRecord, + userHashedIP?: HashedIP } \ No newline at end of file diff --git a/src/utils/getIP.ts b/src/utils/getIP.ts index c6bee7f..a019d84 100644 --- a/src/utils/getIP.ts +++ b/src/utils/getIP.ts @@ -1,19 +1,21 @@ import {config} from '../config'; import {Request} from 'express'; +import { IPAddress } from '../types/segments.model'; -export function getIP(req: Request): string { +export function getIP(req: Request): IPAddress { if (config.behindProxy === true || config.behindProxy === "true") { config.behindProxy = "X-Forwarded-For"; } switch (config.behindProxy as string) { case "X-Forwarded-For": - return req.headers['x-forwarded-for'] as string; + return req.headers['x-forwarded-for'] as IPAddress; case "Cloudflare": - return req.headers['cf-connecting-ip'] as string; + return req.headers['cf-connecting-ip'] as IPAddress; case "X-Real-IP": - return req.headers['x-real-ip'] as string; + return req.headers['x-real-ip'] as IPAddress; default: - return req.connection.remoteAddress; + return req.connection.remoteAddress as IPAddress; } -} + +} \ No newline at end of file