patreon-dl 3.3.0 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -267,6 +267,12 @@ Note the URL shown in the output. Open this URL in a web browser to begin viewin
267
267
 
268
268
  ## Changelog
269
269
 
270
+ v3.3.1
271
+ - Fix bugs affecting library usage:
272
+ - `DB.getInstance()` returning same instance despite different DB path
273
+ - `API.getInstance()` returning same instance despite different DB instance
274
+ - DB not closed when downloader ends or web server stops
275
+
270
276
  v3.3.0
271
277
  - Fix:
272
278
  - YouTube stream fetching error ([patreon-dl-gui#28](https://github.com/patrickkfkan/patreon-dl-gui/issues/28))
@@ -5,7 +5,6 @@ export type APIConstructor = new (...args: any[]) => APIBase;
5
5
  export type APIInstance = InstanceType<typeof API>;
6
6
  export declare class APIBase {
7
7
  name: string;
8
- protected static instance: APIInstance | null;
9
8
  db: DBInstance;
10
9
  logger?: Logger | null;
11
10
  constructor(db: DBInstance, logger?: Logger | null);
@@ -19,10 +19,7 @@ export class APIBase {
19
19
  this.logger = logger;
20
20
  }
21
21
  static getInstance(db, logger) {
22
- if (!this.instance) {
23
- this.instance = new API(db, logger);
24
- }
25
- return this.instance;
22
+ return new API(db, logger);
26
23
  }
27
24
  sanitizeHTML(html) {
28
25
  return _sanitizeHTML(html, SANITIZE_HTML_OPTIONS);
@@ -35,7 +32,6 @@ export class APIBase {
35
32
  commonLog(this.logger, level, this.name, ...msg);
36
33
  }
37
34
  }
38
- APIBase.instance = null;
39
35
  const API = FilterAPIMixin(MediaAPIMixin(SettingsAPIMixin(ContentAPIMixin(CampaignAPIMixin(APIBase)))));
40
36
  export default API;
41
37
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/browse/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,eAAe,CAAC;AAE1C,OAAO,EAAE,SAAS,EAAiB,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAKrD,MAAM,qBAAqB,GAAG;IAC5B,WAAW,EAAE,aAAa,CAAC,QAAQ,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC;IAC/D,iBAAiB,EAAE;QACjB,GAAG,aAAa,CAAC,QAAQ,CAAC,iBAAiB;QAC3C,GAAG,EAAE,CAAC,OAAO,CAAC;KACf;CACF,CAAC;AAEF,MAAM,OAAO,OAAO;IAOlB,YAAY,EAAc,EAAE,MAAsB;QANlD,SAAI,GAAG,KAAK,CAAC;QAOX,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,MAAM,CAAC,WAAW,CAAC,EAAc,EAAE,MAAsB;QACvD,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,IAAI,CAAC,QAAQ,GAAG,IAAI,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACtC,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,YAAY,CAAC,IAAY;QACvB,OAAO,aAAa,CAAC,IAAI,EAAE,qBAAqB,CAAC,CAAC;IACpD,CAAC;IAED,GAAG,CAAC,KAAe,EAAE,GAAG,GAAU;QAChC,MAAM,kBAAkB,GAAG,GAAG,CAAC,IAAI,CACjC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY,KAAK,IAAI,CAAC,CAAC,OAAO,KAAK,oBAAoB,CAChE,CAAC;QACF,IAAI,kBAAkB,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QACD,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,CAAC;IACnD,CAAC;;AA5BgB,gBAAQ,GAAuB,IAAI,AAA3B,CAA4B;AA+BvD,MAAM,GAAG,GAAG,cAAc,CAAC,aAAa,CAAC,gBAAgB,CAAC,eAAe,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAExG,eAAe,GAAG,CAAC","sourcesContent":["import _sanitizeHTML from 'sanitize-html';\nimport type Logger from '../../utils/logging/Logger.js';\nimport { commonLog, type LogLevel } from '../../utils/logging/Logger.js';\nimport { CampaignAPIMixin } from './CampaignAPIMixin.js';\nimport { type DBInstance } from '../db';\nimport { ContentAPIMixin } from './ContentAPIMixin.js';\nimport { SettingsAPIMixin } from './SettingsAPIMixin.js';\nimport { MediaAPIMixin } from './MediaAPIMixin.js';\nimport { FilterAPIMixin } from './FilterAPIMixin.js';\n\nexport type APIConstructor = new (...args: any[]) => APIBase;\nexport type APIInstance = InstanceType<typeof API>;\n\nconst SANITIZE_HTML_OPTIONS = {\n allowedTags: _sanitizeHTML.defaults.allowedTags.concat(['img']),\n allowedAttributes: {\n ..._sanitizeHTML.defaults.allowedAttributes,\n '*': ['class']\n }\n};\n\nexport class APIBase {\n name = 'API';\n\n protected static instance: APIInstance | null = null;\n db: DBInstance;\n logger?: Logger | null;\n\n constructor(db: DBInstance, logger?: Logger | null) {\n this.db = db;\n this.logger = logger;\n }\n\n static getInstance(db: DBInstance, logger?: Logger | null) {\n if (!this.instance) {\n this.instance = new API(db, logger);\n }\n return this.instance;\n }\n\n sanitizeHTML(html: string) {\n return _sanitizeHTML(html, SANITIZE_HTML_OPTIONS);\n }\n\n log(level: LogLevel, ...msg: any[]) {\n const limiterStopOnError = msg.find(\n (m) => m instanceof Error && m.message === 'LimiterStopOnError'\n );\n if (limiterStopOnError) {\n return;\n }\n commonLog(this.logger, level, this.name, ...msg);\n }\n}\n\nconst API = FilterAPIMixin(MediaAPIMixin(SettingsAPIMixin(ContentAPIMixin(CampaignAPIMixin(APIBase)))));\n\nexport default API;\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/browse/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,eAAe,CAAC;AAE1C,OAAO,EAAE,SAAS,EAAiB,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAKrD,MAAM,qBAAqB,GAAG;IAC5B,WAAW,EAAE,aAAa,CAAC,QAAQ,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC;IAC/D,iBAAiB,EAAE;QACjB,GAAG,aAAa,CAAC,QAAQ,CAAC,iBAAiB;QAC3C,GAAG,EAAE,CAAC,OAAO,CAAC;KACf;CACF,CAAC;AAEF,MAAM,OAAO,OAAO;IAMlB,YAAY,EAAc,EAAE,MAAsB;QALlD,SAAI,GAAG,KAAK,CAAC;QAMX,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,MAAM,CAAC,WAAW,CAAC,EAAc,EAAE,MAAsB;QACvD,OAAO,IAAI,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAC7B,CAAC;IAED,YAAY,CAAC,IAAY;QACvB,OAAO,aAAa,CAAC,IAAI,EAAE,qBAAqB,CAAC,CAAC;IACpD,CAAC;IAED,GAAG,CAAC,KAAe,EAAE,GAAG,GAAU;QAChC,MAAM,kBAAkB,GAAG,GAAG,CAAC,IAAI,CACjC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY,KAAK,IAAI,CAAC,CAAC,OAAO,KAAK,oBAAoB,CAChE,CAAC;QACF,IAAI,kBAAkB,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QACD,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,CAAC;IACnD,CAAC;CACF;AAED,MAAM,GAAG,GAAG,cAAc,CAAC,aAAa,CAAC,gBAAgB,CAAC,eAAe,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAExG,eAAe,GAAG,CAAC","sourcesContent":["import _sanitizeHTML from 'sanitize-html';\nimport type Logger from '../../utils/logging/Logger.js';\nimport { commonLog, type LogLevel } from '../../utils/logging/Logger.js';\nimport { CampaignAPIMixin } from './CampaignAPIMixin.js';\nimport { type DBInstance } from '../db';\nimport { ContentAPIMixin } from './ContentAPIMixin.js';\nimport { SettingsAPIMixin } from './SettingsAPIMixin.js';\nimport { MediaAPIMixin } from './MediaAPIMixin.js';\nimport { FilterAPIMixin } from './FilterAPIMixin.js';\n\nexport type APIConstructor = new (...args: any[]) => APIBase;\nexport type APIInstance = InstanceType<typeof API>;\n\nconst SANITIZE_HTML_OPTIONS = {\n allowedTags: _sanitizeHTML.defaults.allowedTags.concat(['img']),\n allowedAttributes: {\n ..._sanitizeHTML.defaults.allowedAttributes,\n '*': ['class']\n }\n};\n\nexport class APIBase {\n name = 'API';\n\n db: DBInstance;\n logger?: Logger | null;\n\n constructor(db: DBInstance, logger?: Logger | null) {\n this.db = db;\n this.logger = logger;\n }\n\n static getInstance(db: DBInstance, logger?: Logger | null) {\n return new API(db, logger);\n }\n\n sanitizeHTML(html: string) {\n return _sanitizeHTML(html, SANITIZE_HTML_OPTIONS);\n }\n\n log(level: LogLevel, ...msg: any[]) {\n const limiterStopOnError = msg.find(\n (m) => m instanceof Error && m.message === 'LimiterStopOnError'\n );\n if (limiterStopOnError) {\n return;\n }\n commonLog(this.logger, level, this.name, ...msg);\n }\n}\n\nconst API = FilterAPIMixin(MediaAPIMixin(SettingsAPIMixin(ContentAPIMixin(CampaignAPIMixin(APIBase)))));\n\nexport default API;\n"]}
@@ -45,6 +45,7 @@ export declare function CampaignDBMixin<TBase extends UserDBConstructor>(Base: T
45
45
  limitOffset?: string;
46
46
  }): string;
47
47
  name: string;
48
+ dbPath: string | null;
48
49
  db: import("better-sqlite3").Database;
49
50
  logger?: import("../../index.js").Logger | null;
50
51
  exec(sql: string): void;
@@ -84,6 +84,7 @@ export declare function ContentDBMixin<TBase extends CampaignDBConstructor>(Base
84
84
  limitOffset?: string;
85
85
  }): string;
86
86
  name: string;
87
+ dbPath: string | null;
87
88
  db: import("better-sqlite3").Database;
88
89
  logger?: import("../..").Logger | null;
89
90
  exec(sql: string): void;
@@ -6,6 +6,7 @@ export declare function EnvDBMixin<TBase extends DBConstructor>(Base: TBase): {
6
6
  getEnvValue<T = any>(key: string): T | null;
7
7
  checkEnvExists(key: string): boolean;
8
8
  name: string;
9
+ dbPath: string | null;
9
10
  db: import("better-sqlite3").Database;
10
11
  logger?: import("../..").Logger | null;
11
12
  exec(sql: string): void;
@@ -35,6 +35,7 @@ export declare function MediaDBMixin<TBase extends DBConstructor>(Base: TBase):
35
35
  limitOffset?: string;
36
36
  }): string;
37
37
  name: string;
38
+ dbPath: string | null;
38
39
  db: import("better-sqlite3").Database;
39
40
  logger?: import("../..").Logger | null;
40
41
  exec(sql: string): void;
@@ -36,6 +36,7 @@ export declare function UserDBMixin<TBase extends MediaDBConstructor>(Base: TBas
36
36
  limitOffset?: string;
37
37
  }): string;
38
38
  name: string;
39
+ dbPath: string | null;
39
40
  db: import("better-sqlite3").Database;
40
41
  logger?: import("../..").Logger | null;
41
42
  exec(sql: string): void;
@@ -3,246 +3,20 @@ import type Logger from '../../utils/logging/Logger.js';
3
3
  import { type LogLevel } from '../../utils/logging/Logger.js';
4
4
  export type DBConstructor = new (...args: any[]) => DBBase;
5
5
  export type DBInstance = InstanceType<typeof DB>;
6
+ interface DBPool {
7
+ [dbPath: string]: {
8
+ db: Database.Database;
9
+ count: number;
10
+ } | undefined;
11
+ }
6
12
  export declare class DBBase {
7
13
  name: string;
8
- static instance: DBInstance | null;
14
+ dbPath: string | null;
15
+ static dbPool: DBPool;
9
16
  db: Database.Database;
10
17
  logger?: Logger | null;
11
- constructor(db: Database.Database, logger?: Logger | null);
12
- static getInstance(file: string, dryRun?: boolean, logger?: Logger | null): Promise<{
13
- saveEnvValue(key: string, value: any): void;
14
- getEnvValue<T = any>(key: string): T | null;
15
- checkEnvExists(key: string): boolean;
16
- name: string;
17
- db: Database.Database;
18
- logger?: Logger | null;
19
- exec(sql: string): void;
20
- run(sql: string, params?: any[]): Database.RunResult;
21
- get<T = any>(sql: string, params?: any[]): T | undefined;
22
- all<T = any>(sql: string, params?: any[]): T[];
23
- close(): void;
24
- transaction(fn: (...args: any[]) => any): (...args: any[]) => any;
25
- log(level: LogLevel, ...msg: any[]): void;
26
- } & {
27
- saveContent(content: import("../../index.js").Post | import("../../index.js").Product): void;
28
- "__#120@#saveContentMedia"(content: import("../../index.js").Post | import("../../index.js").Product): void;
29
- "__#120@#savepostMedia"(post: import("../../index.js").Post): void;
30
- "__#120@#saveProductMedia"(product: import("../../index.js").Product): void;
31
- "__#120@#doSaveContentMedia"(content: import("../../index.js").Post | import("../../index.js").Product, media: import("../../index.js").Downloadable, mediaIndex: number, isPreview: boolean): void;
32
- "__#120@#publishedAtToTime"(publishedAt: string | null): number | null;
33
- "__#120@#savePostTiers"(post: import("../../index.js").Post): void;
34
- "__#120@#doSaveTier"(post: import("../../index.js").Post, tier: import("../../index.js").Tier): void;
35
- savePostComments(post: import("../../index.js").Post, comments: import("../../index.js").Comment[]): void;
36
- checkPostCommentsExist(post: import("../../index.js").Post): boolean;
37
- getContent(id: string, contentType: "post"): import("../types/Content.js").PostWithComments | null;
38
- getContent(id: string, contentType: "product"): import("../../index.js").Product | null;
39
- getPreviousNextContent<T extends import("../types/Content.js").ContentType>(content: import("../../index.js").Post | import("../../index.js").Product, context: import("../types/Content.js").GetContentContext<T>): import("../types/Content.js").GetPreviousNextContentResult<T>;
40
- getContentList<T extends import("../types/Content.js").ContentType>(params: import("../types/Content.js").GetContentListParams<T>, db?: {
41
- whereClauses: string[];
42
- whereValues: any[];
43
- } | undefined, includeTotal?: boolean): import("../types/Content.js").ContentList<T>;
44
- "__#120@#parseContentRowJoinedComments"(row: any): any;
45
- getContentCountByDate(contentType: import("../types/Content.js").ContentType, groupBy: "year" | "month", filter?: {
46
- campaign?: import("../../index.js").Campaign | string | null;
47
- date?: Date | null;
48
- } | undefined): {
49
- dt: string;
50
- count: number;
51
- }[];
52
- getPostCountByType(campaign: import("../../index.js").Campaign | string): {
53
- postType: string;
54
- count: number;
55
- }[];
56
- getPostCountByTier(campaign: import("../../index.js").Campaign | string): {
57
- tierId: string;
58
- title: string;
59
- count: number;
60
- }[];
61
- checkContentExists(id: string, contentType: import("../types/Content.js").ContentType, campaign: import("../../index.js").Campaign | null): boolean;
62
- getPostComments(post: import("../../index.js").Post | string): import("../../index.js").Comment[] | null;
63
- saveCampaign(campaign: import("../../index.js").Campaign | null, downloadDate: Date, overwriteIfExists?: boolean): void;
64
- getCampaign(params: import("../types/Campaign.js").GetCampaignParams): import("../../index.js").Campaign | null;
65
- "__#119@#saveRewards"(campaign: import("../../index.js").Campaign): void;
66
- "__#119@#doSaveReward"(campaign: import("../../index.js").Campaign, reward: import("../../index.js").Reward): void;
67
- getCampaignList(params: import("../types/Campaign.js").GetCampaignListParams): import("../types/Campaign.js").CampaignList;
68
- "__#119@#getCampaignWithCounts"(params: import("../types/Campaign.js").GetCampaignParams): import("../types/Campaign.js").CampaignWithCounts | null;
69
- checkCampaignExists(id: string): boolean;
70
- saveUser(user: import("../../index.js").User | null): void;
71
- getUserByID(id: string): import("../../index.js").User | null;
72
- checkUserExists(id: string): boolean;
73
- saveMedia(media: import("../../index.js").Downloadable): void;
74
- checkMediaExists(media: import("../../index.js").Downloadable): boolean;
75
- checkContentMediaExists(content: import("../../index.js").Post | import("../../index.js").Product, media: import("../../index.js").Downloadable): boolean;
76
- getMediaByID(id: string): import("../../index.js").Downloaded | null;
77
- getMediaList<T extends import("../types/Content.js").ContentType>(params: import("../types/Media.js").GetMediaListParams<T>): import("../types/Media.js").MediaList<T>;
78
- getMediaCountByDate(groupBy: "year" | "month", filter?: {
79
- campaign?: import("../../index.js").Campaign | string | null;
80
- date?: Date | null;
81
- } | undefined): {
82
- dt: string;
83
- count: number;
84
- }[];
85
- getMediaCountByContentType(campaign?: import("../../index.js").Campaign | string | null): {
86
- contentType: "post" | "product";
87
- count: number;
88
- }[];
89
- getMediaCountByTier(campaign: import("../../index.js").Campaign | string): {
90
- tierId: string;
91
- title: string;
92
- count: number;
93
- }[];
94
- getMediaListSQL(params: {
95
- select: string;
96
- join?: string;
97
- where?: string;
98
- groupBy?: string;
99
- orderBy?: string;
100
- limitOffset?: string;
101
- }): string;
102
- name: string;
103
- db: Database.Database;
104
- logger?: Logger | null;
105
- exec(sql: string): void;
106
- run(sql: string, params?: any[]): Database.RunResult;
107
- get<T = any>(sql: string, params?: any[]): T | undefined;
108
- all<T = any>(sql: string, params?: any[]): T[];
109
- close(): void;
110
- transaction(fn: (...args: any[]) => any): (...args: any[]) => any;
111
- log(level: LogLevel, ...msg: any[]): void;
112
- } & {
113
- saveCampaign(campaign: import("../../index.js").Campaign | null, downloadDate: Date, overwriteIfExists?: boolean): void;
114
- getCampaign(params: import("../types/Campaign.js").GetCampaignParams): import("../../index.js").Campaign | null;
115
- "__#119@#saveRewards"(campaign: import("../../index.js").Campaign): void;
116
- "__#119@#doSaveReward"(campaign: import("../../index.js").Campaign, reward: import("../../index.js").Reward): void;
117
- getCampaignList(params: import("../types/Campaign.js").GetCampaignListParams): import("../types/Campaign.js").CampaignList;
118
- "__#119@#getCampaignWithCounts"(params: import("../types/Campaign.js").GetCampaignParams): import("../types/Campaign.js").CampaignWithCounts | null;
119
- checkCampaignExists(id: string): boolean;
120
- saveUser(user: import("../../index.js").User | null): void;
121
- getUserByID(id: string): import("../../index.js").User | null;
122
- checkUserExists(id: string): boolean;
123
- saveMedia(media: import("../../index.js").Downloadable): void;
124
- checkMediaExists(media: import("../../index.js").Downloadable): boolean;
125
- checkContentMediaExists(content: import("../../index.js").Post | import("../../index.js").Product, media: import("../../index.js").Downloadable): boolean;
126
- getMediaByID(id: string): import("../../index.js").Downloaded | null;
127
- getMediaList<T extends import("../types/Content.js").ContentType>(params: import("../types/Media.js").GetMediaListParams<T>): import("../types/Media.js").MediaList<T>;
128
- getMediaCountByDate(groupBy: "year" | "month", filter?: {
129
- campaign?: import("../../index.js").Campaign | string | null;
130
- date?: Date | null;
131
- } | undefined): {
132
- dt: string;
133
- count: number;
134
- }[];
135
- getMediaCountByContentType(campaign?: import("../../index.js").Campaign | string | null): {
136
- contentType: "post" | "product";
137
- count: number;
138
- }[];
139
- getMediaCountByTier(campaign: import("../../index.js").Campaign | string): {
140
- tierId: string;
141
- title: string;
142
- count: number;
143
- }[];
144
- getMediaListSQL(params: {
145
- select: string;
146
- join?: string;
147
- where?: string;
148
- groupBy?: string;
149
- orderBy?: string;
150
- limitOffset?: string;
151
- }): string;
152
- name: string;
153
- db: Database.Database;
154
- logger?: Logger | null;
155
- exec(sql: string): void;
156
- run(sql: string, params?: any[]): Database.RunResult;
157
- get<T = any>(sql: string, params?: any[]): T | undefined;
158
- all<T = any>(sql: string, params?: any[]): T[];
159
- close(): void;
160
- transaction(fn: (...args: any[]) => any): (...args: any[]) => any;
161
- log(level: LogLevel, ...msg: any[]): void;
162
- } & {
163
- saveUser(user: import("../../index.js").User | null): void;
164
- getUserByID(id: string): import("../../index.js").User | null;
165
- checkUserExists(id: string): boolean;
166
- saveMedia(media: import("../../index.js").Downloadable): void;
167
- checkMediaExists(media: import("../../index.js").Downloadable): boolean;
168
- checkContentMediaExists(content: import("../../index.js").Post | import("../../index.js").Product, media: import("../../index.js").Downloadable): boolean;
169
- getMediaByID(id: string): import("../../index.js").Downloaded | null;
170
- getMediaList<T extends import("../types/Content.js").ContentType>(params: import("../types/Media.js").GetMediaListParams<T>): import("../types/Media.js").MediaList<T>;
171
- getMediaCountByDate(groupBy: "year" | "month", filter?: {
172
- campaign?: import("../../index.js").Campaign | string | null;
173
- date?: Date | null;
174
- } | undefined): {
175
- dt: string;
176
- count: number;
177
- }[];
178
- getMediaCountByContentType(campaign?: import("../../index.js").Campaign | string | null): {
179
- contentType: "post" | "product";
180
- count: number;
181
- }[];
182
- getMediaCountByTier(campaign: import("../../index.js").Campaign | string): {
183
- tierId: string;
184
- title: string;
185
- count: number;
186
- }[];
187
- getMediaListSQL(params: {
188
- select: string;
189
- join?: string;
190
- where?: string;
191
- groupBy?: string;
192
- orderBy?: string;
193
- limitOffset?: string;
194
- }): string;
195
- name: string;
196
- db: Database.Database;
197
- logger?: Logger | null;
198
- exec(sql: string): void;
199
- run(sql: string, params?: any[]): Database.RunResult;
200
- get<T = any>(sql: string, params?: any[]): T | undefined;
201
- all<T = any>(sql: string, params?: any[]): T[];
202
- close(): void;
203
- transaction(fn: (...args: any[]) => any): (...args: any[]) => any;
204
- log(level: LogLevel, ...msg: any[]): void;
205
- } & {
206
- saveMedia(media: import("../../index.js").Downloadable): void;
207
- checkMediaExists(media: import("../../index.js").Downloadable): boolean;
208
- checkContentMediaExists(content: import("../../index.js").Post | import("../../index.js").Product, media: import("../../index.js").Downloadable): boolean;
209
- getMediaByID(id: string): import("../../index.js").Downloaded | null;
210
- getMediaList<T extends import("../types/Content.js").ContentType>(params: import("../types/Media.js").GetMediaListParams<T>): import("../types/Media.js").MediaList<T>;
211
- getMediaCountByDate(groupBy: "year" | "month", filter?: {
212
- campaign?: import("../../index.js").Campaign | string | null;
213
- date?: Date | null;
214
- } | undefined): {
215
- dt: string;
216
- count: number;
217
- }[];
218
- getMediaCountByContentType(campaign?: import("../../index.js").Campaign | string | null): {
219
- contentType: "post" | "product";
220
- count: number;
221
- }[];
222
- getMediaCountByTier(campaign: import("../../index.js").Campaign | string): {
223
- tierId: string;
224
- title: string;
225
- count: number;
226
- }[];
227
- getMediaListSQL(params: {
228
- select: string;
229
- join?: string;
230
- where?: string;
231
- groupBy?: string;
232
- orderBy?: string;
233
- limitOffset?: string;
234
- }): string;
235
- name: string;
236
- db: Database.Database;
237
- logger?: Logger | null;
238
- exec(sql: string): void;
239
- run(sql: string, params?: any[]): Database.RunResult;
240
- get<T = any>(sql: string, params?: any[]): T | undefined;
241
- all<T = any>(sql: string, params?: any[]): T[];
242
- close(): void;
243
- transaction(fn: (...args: any[]) => any): (...args: any[]) => any;
244
- log(level: LogLevel, ...msg: any[]): void;
245
- } & DBBase>;
18
+ constructor(dbPath: string | null, db: Database.Database, logger?: Logger | null);
19
+ static getInstance(file: string, dryRun?: boolean, logger?: Logger | null): Promise<DBInstance>;
246
20
  exec(sql: string): void;
247
21
  run(sql: string, params?: any[]): Database.RunResult;
248
22
  get<T = any>(sql: string, params?: any[]): T | undefined;
@@ -257,6 +31,7 @@ declare const DB: {
257
31
  getEnvValue<T = any>(key: string): T | null;
258
32
  checkEnvExists(key: string): boolean;
259
33
  name: string;
34
+ dbPath: string | null;
260
35
  db: Database.Database;
261
36
  logger?: Logger | null;
262
37
  exec(sql: string): void;
@@ -345,6 +120,7 @@ declare const DB: {
345
120
  limitOffset?: string;
346
121
  }): string;
347
122
  name: string;
123
+ dbPath: string | null;
348
124
  db: Database.Database;
349
125
  logger?: Logger | null;
350
126
  exec(sql: string): void;
@@ -397,6 +173,7 @@ declare const DB: {
397
173
  limitOffset?: string;
398
174
  }): string;
399
175
  name: string;
176
+ dbPath: string | null;
400
177
  db: Database.Database;
401
178
  logger?: Logger | null;
402
179
  exec(sql: string): void;
@@ -442,6 +219,7 @@ declare const DB: {
442
219
  limitOffset?: string;
443
220
  }): string;
444
221
  name: string;
222
+ dbPath: string | null;
445
223
  db: Database.Database;
446
224
  logger?: Logger | null;
447
225
  exec(sql: string): void;
@@ -484,6 +262,7 @@ declare const DB: {
484
262
  limitOffset?: string;
485
263
  }): string;
486
264
  name: string;
265
+ dbPath: string | null;
487
266
  db: Database.Database;
488
267
  logger?: Logger | null;
489
268
  exec(sql: string): void;
@@ -5,18 +5,39 @@ import { UserDBMixin } from './UserDBMixin.js';
5
5
  import { CampaignDBMixin } from './CampaignDBMixin.js';
6
6
  import { ContentDBMixin } from './ContentDBMixin.js';
7
7
  import { EnvDBMixin } from './EnvDBMixin.js';
8
+ import path from 'path';
8
9
  export class DBBase {
9
- constructor(db, logger) {
10
+ constructor(dbPath, db, logger) {
10
11
  this.name = 'DB';
12
+ this.dbPath = dbPath;
11
13
  this.db = db;
12
14
  this.logger = logger;
13
15
  }
14
16
  static async getInstance(file, dryRun = false, logger) {
15
- if (!this.instance) {
16
- const db = await openDB(file, dryRun, logger);
17
- this.instance = new DB(db, logger);
17
+ let db;
18
+ let dbPath;
19
+ if (dryRun) {
20
+ dbPath = null;
21
+ db = await openDB(file, dryRun, logger);
18
22
  }
19
- return this.instance;
23
+ else {
24
+ dbPath = path.resolve(file);
25
+ const poolEntry = this.dbPool[dbPath];
26
+ if (poolEntry) {
27
+ db = poolEntry.db;
28
+ poolEntry.count++;
29
+ commonLog(logger, 'debug', 'DB', `Obtained DB from pool (shares: ${poolEntry.count})`);
30
+ }
31
+ else {
32
+ db = await openDB(file, dryRun, logger);
33
+ this.dbPool[dbPath] = {
34
+ db,
35
+ count: 1
36
+ };
37
+ commonLog(logger, 'debug', 'DB', `Added DB to pool`);
38
+ }
39
+ }
40
+ return new DB(dbPath, db, logger);
20
41
  }
21
42
  exec(sql) {
22
43
  this.db.exec(sql);
@@ -35,7 +56,23 @@ export class DBBase {
35
56
  return (params ? stmt.all(...params) : stmt.all());
36
57
  }
37
58
  close() {
38
- this.db.close();
59
+ if (!this.dbPath) {
60
+ this.log('debug', '(dry-run) Close in-memory DB');
61
+ this.db.close();
62
+ return;
63
+ }
64
+ const poolEntry = DBBase.dbPool[this.dbPath];
65
+ if (poolEntry && poolEntry.count > 0) {
66
+ poolEntry.count--;
67
+ if (poolEntry.count === 0) {
68
+ this.log('debug', 'Close DB (no shares remaining)');
69
+ delete DBBase.dbPool[this.dbPath];
70
+ this.db.close();
71
+ }
72
+ else {
73
+ this.log('debug', `Close DB skipped - ${poolEntry.count} shares remaining`);
74
+ }
75
+ }
39
76
  }
40
77
  transaction(fn) {
41
78
  return this.db.transaction(fn);
@@ -48,7 +85,7 @@ export class DBBase {
48
85
  commonLog(this.logger, level, this.name, ...msg);
49
86
  }
50
87
  }
51
- DBBase.instance = null;
88
+ DBBase.dbPool = {};
52
89
  const DB = EnvDBMixin(ContentDBMixin(CampaignDBMixin(UserDBMixin(MediaDBMixin(DBBase)))));
53
90
  export default DB;
54
91
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/browse/db/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type Database from 'better-sqlite3';\nimport type Logger from '../../utils/logging/Logger.js';\nimport { commonLog, type LogLevel } from '../../utils/logging/Logger.js';\nimport { openDB } from './Init.js';\nimport { MediaDBMixin } from './MediaDBMixin.js';\nimport { UserDBMixin } from './UserDBMixin.js';\nimport { CampaignDBMixin } from './CampaignDBMixin.js';\nimport { ContentDBMixin } from './ContentDBMixin.js';\nimport { EnvDBMixin } from './EnvDBMixin.js';\n\nexport type DBConstructor = new (...args: any[]) => DBBase;\nexport type DBInstance = InstanceType<typeof DB>;\n\nexport class DBBase {\n name = 'DB';\n\n static instance: DBInstance | null = null;\n db: Database.Database;\n logger?: Logger | null;\n\n constructor(db: Database.Database, logger?: Logger | null) {\n this.db = db;\n this.logger = logger;\n }\n\n static async getInstance(file: string, dryRun = false, logger?: Logger | null) {\n if (!this.instance) {\n const db = await openDB(file, dryRun, logger);\n this.instance = new DB(db, logger);\n }\n return this.instance;\n }\n\n exec(sql: string): void {\n this.db.exec(sql);\n }\n\n run(sql: string, params?: any[]): Database.RunResult {\n const stmt = this.db.prepare(sql);\n return params ? stmt.run(...params) : stmt.run();\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters\n get<T = any>(sql: string, params?: any[]): T | undefined {\n const stmt = this.db.prepare(sql);\n return (params ? stmt.get(...params) : stmt.get()) as T | undefined;\n }\n\n all<T = any>(sql: string, params?: any[]): T[] {\n const stmt = this.db.prepare(sql);\n return (params ? stmt.all(...params) : stmt.all()) as T[];\n }\n\n close(): void {\n this.db.close();\n }\n\n transaction(fn: (...args: any[]) => any): (...args: any[]) => any {\n return this.db.transaction(fn);\n }\n\n log(level: LogLevel, ...msg: any[]) {\n const limiterStopOnError = msg.find(\n (m) => m instanceof Error && m.message === 'LimiterStopOnError'\n );\n if (limiterStopOnError) {\n return;\n }\n commonLog(this.logger, level, this.name, ...msg);\n }\n}\n\nconst DB = EnvDBMixin(\n ContentDBMixin(\n CampaignDBMixin(\n UserDBMixin(\n MediaDBMixin(DBBase)\n )\n )\n )\n);\n\nexport default DB;"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/browse/db/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAiB,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,IAAI,MAAM,MAAM,CAAC;AASxB,MAAM,OAAO,MAAM;IAQjB,YAAY,MAAqB,EAAE,EAAqB,EAAE,MAAsB;QAPhF,SAAI,GAAG,IAAI,CAAC;QAQV,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,IAAY,EAAE,MAAM,GAAG,KAAK,EAAE,MAAsB;QAC3E,IAAI,EAAqB,CAAC;QAC1B,IAAI,MAAqB,CAAC;QAC1B,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,GAAG,IAAI,CAAC;YACd,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAC1C,CAAC;aACI,CAAC;YACJ,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACtC,IAAI,SAAS,EAAE,CAAC;gBACd,EAAE,GAAG,SAAS,CAAC,EAAE,CAAC;gBAClB,SAAS,CAAC,KAAK,EAAE,CAAC;gBAClB,SAAS,CACP,MAAM,EACN,OAAO,EACP,IAAI,EACJ,kCAAkC,SAAS,CAAC,KAAK,GAAG,CACrD,CAAC;YACJ,CAAC;iBACI,CAAC;gBACJ,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;gBACxC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG;oBACpB,EAAE;oBACF,KAAK,EAAE,CAAC;iBACT,CAAC;gBACF,SAAS,CACP,MAAM,EACN,OAAO,EACP,IAAI,EACJ,kBAAkB,CACnB,CAAC;YACJ,CAAC;QACH,CAAC;QACD,OAAO,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAED,IAAI,CAAC,GAAW;QACd,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpB,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,MAAc;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAClC,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IACnD,CAAC;IAED,6EAA6E;IAC7E,GAAG,CAAU,GAAW,EAAE,MAAc;QACtC,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAClC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAkB,CAAC;IACtE,CAAC;IAED,GAAG,CAAU,GAAW,EAAE,MAAc;QACtC,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAClC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAQ,CAAC;IAC5D,CAAC;IAED,KAAK;QACH,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,8BAA8B,CAAC,CAAC;YAClD,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;YAChB,OAAO;QACT,CAAC;QACD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7C,IAAI,SAAS,IAAI,SAAS,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;YACrC,SAAS,CAAC,KAAK,EAAE,CAAC;YAClB,IAAI,SAAS,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;gBAC1B,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,gCAAgC,CAAC,CAAC;gBACpD,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAClC,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;YAClB,CAAC;iBACI,CAAC;gBACJ,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,sBAAsB,SAAS,CAAC,KAAK,mBAAmB,CAAC,CAAA;YAC7E,CAAC;QACH,CAAC;IACH,CAAC;IAED,WAAW,CAAC,EAA2B;QACrC,OAAO,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC;IAED,GAAG,CAAC,KAAe,EAAE,GAAG,GAAU;QAChC,MAAM,kBAAkB,GAAG,GAAG,CAAC,IAAI,CACjC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY,KAAK,IAAI,CAAC,CAAC,OAAO,KAAK,oBAAoB,CAChE,CAAC;QACF,IAAI,kBAAkB,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QACD,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,CAAC;IACnD,CAAC;;AAnGM,aAAM,GAAW,EAAE,AAAb,CAAc;AAsG7B,MAAM,EAAE,GAAG,UAAU,CACnB,cAAc,CACZ,eAAe,CACb,WAAW,CACT,YAAY,CAAC,MAAM,CAAC,CACrB,CACF,CACF,CACF,CAAC;AAEF,eAAe,EAAE,CAAC","sourcesContent":["import type Database from 'better-sqlite3';\nimport type Logger from '../../utils/logging/Logger.js';\nimport { commonLog, type LogLevel } from '../../utils/logging/Logger.js';\nimport { openDB } from './Init.js';\nimport { MediaDBMixin } from './MediaDBMixin.js';\nimport { UserDBMixin } from './UserDBMixin.js';\nimport { CampaignDBMixin } from './CampaignDBMixin.js';\nimport { ContentDBMixin } from './ContentDBMixin.js';\nimport { EnvDBMixin } from './EnvDBMixin.js';\nimport path from 'path';\n\nexport type DBConstructor = new (...args: any[]) => DBBase;\nexport type DBInstance = InstanceType<typeof DB>;\n\ninterface DBPool {\n [dbPath: string]: { db: Database.Database; count: number; } | undefined;\n}\n\nexport class DBBase {\n name = 'DB';\n\n dbPath: string | null;\n static dbPool: DBPool = {};\n db: Database.Database;\n logger?: Logger | null;\n\n constructor(dbPath: string | null, db: Database.Database, logger?: Logger | null) {\n this.dbPath = dbPath;\n this.db = db;\n this.logger = logger;\n }\n\n static async getInstance(file: string, dryRun = false, logger?: Logger | null): Promise<DBInstance> {\n let db: Database.Database;\n let dbPath: string | null;\n if (dryRun) {\n dbPath = null;\n db = await openDB(file, dryRun, logger);\n }\n else {\n dbPath = path.resolve(file);\n const poolEntry = this.dbPool[dbPath];\n if (poolEntry) {\n db = poolEntry.db;\n poolEntry.count++;\n commonLog(\n logger,\n 'debug',\n 'DB',\n `Obtained DB from pool (shares: ${poolEntry.count})`\n );\n }\n else {\n db = await openDB(file, dryRun, logger);\n this.dbPool[dbPath] = {\n db,\n count: 1\n };\n commonLog(\n logger,\n 'debug',\n 'DB',\n `Added DB to pool`\n );\n }\n }\n return new DB(dbPath, db, logger);\n }\n\n exec(sql: string): void {\n this.db.exec(sql);\n }\n\n run(sql: string, params?: any[]): Database.RunResult {\n const stmt = this.db.prepare(sql);\n return params ? stmt.run(...params) : stmt.run();\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters\n get<T = any>(sql: string, params?: any[]): T | undefined {\n const stmt = this.db.prepare(sql);\n return (params ? stmt.get(...params) : stmt.get()) as T | undefined;\n }\n\n all<T = any>(sql: string, params?: any[]): T[] {\n const stmt = this.db.prepare(sql);\n return (params ? stmt.all(...params) : stmt.all()) as T[];\n }\n\n close(): void {\n if (!this.dbPath) {\n this.log('debug', '(dry-run) Close in-memory DB');\n this.db.close();\n return;\n }\n const poolEntry = DBBase.dbPool[this.dbPath];\n if (poolEntry && poolEntry.count > 0) {\n poolEntry.count--;\n if (poolEntry.count === 0) {\n this.log('debug', 'Close DB (no shares remaining)');\n delete DBBase.dbPool[this.dbPath];\n this.db.close();\n }\n else {\n this.log('debug', `Close DB skipped - ${poolEntry.count} shares remaining`)\n }\n }\n }\n\n transaction(fn: (...args: any[]) => any): (...args: any[]) => any {\n return this.db.transaction(fn);\n }\n\n log(level: LogLevel, ...msg: any[]) {\n const limiterStopOnError = msg.find(\n (m) => m instanceof Error && m.message === 'LimiterStopOnError'\n );\n if (limiterStopOnError) {\n return;\n }\n commonLog(this.logger, level, this.name, ...msg);\n }\n}\n\nconst DB = EnvDBMixin(\n ContentDBMixin(\n CampaignDBMixin(\n UserDBMixin(\n MediaDBMixin(DBBase)\n )\n )\n )\n);\n\nexport default DB;"]}
@@ -9,7 +9,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _WebServer_instances, _WebServer_config, _WebServer_app, _WebServer_server, _WebServer_status, _WebServer_port, _WebServer_getPort;
12
+ var _WebServer_instances, _WebServer_config, _WebServer_app, _WebServer_server, _WebServer_status, _WebServer_port, _WebServer_db, _WebServer_getPort;
13
13
  import express from 'express';
14
14
  import path from 'path';
15
15
  import fs from 'fs';
@@ -27,11 +27,13 @@ export class WebServer {
27
27
  _WebServer_server.set(this, void 0);
28
28
  _WebServer_status.set(this, void 0);
29
29
  _WebServer_port.set(this, void 0);
30
+ _WebServer_db.set(this, void 0);
30
31
  __classPrivateFieldSet(this, _WebServer_config, config, "f");
31
32
  __classPrivateFieldSet(this, _WebServer_app, express(), "f");
32
33
  __classPrivateFieldSet(this, _WebServer_server, null, "f");
33
34
  __classPrivateFieldSet(this, _WebServer_status, 'stopped', "f");
34
35
  __classPrivateFieldSet(this, _WebServer_port, config.port || null, "f");
36
+ __classPrivateFieldSet(this, _WebServer_db, null, "f");
35
37
  }
36
38
  async start() {
37
39
  if (__classPrivateFieldGet(this, _WebServer_status, "f") === 'started') {
@@ -57,6 +59,7 @@ export class WebServer {
57
59
  __classPrivateFieldGet(this, _WebServer_app, "f").use('/themes', express.static(path.resolve(import.meta.dirname, '../web/themes')));
58
60
  __classPrivateFieldGet(this, _WebServer_app, "f").use('/images', express.static(path.resolve(import.meta.dirname, '../web/images')));
59
61
  __classPrivateFieldGet(this, _WebServer_app, "f").use(router);
62
+ __classPrivateFieldSet(this, _WebServer_db, db, "f");
60
63
  __classPrivateFieldSet(this, _WebServer_port, await __classPrivateFieldGet(this, _WebServer_instances, "m", _WebServer_getPort).call(this), "f");
61
64
  return new Promise((resolve, reject) => {
62
65
  __classPrivateFieldSet(this, _WebServer_server, __classPrivateFieldGet(this, _WebServer_app, "f").listen(__classPrivateFieldGet(this, _WebServer_port, "f"), (error) => {
@@ -77,12 +80,20 @@ export class WebServer {
77
80
  if (__classPrivateFieldGet(this, _WebServer_server, "f")) {
78
81
  __classPrivateFieldGet(this, _WebServer_server, "f").close((error) => {
79
82
  if (error) {
80
- reject(error);
81
- return;
83
+ return reject(error);
84
+ ;
82
85
  }
83
86
  __classPrivateFieldSet(this, _WebServer_server, null, "f");
84
87
  __classPrivateFieldSet(this, _WebServer_port, null, "f");
85
88
  __classPrivateFieldSet(this, _WebServer_status, 'stopped', "f");
89
+ if (__classPrivateFieldGet(this, _WebServer_db, "f")) {
90
+ try {
91
+ __classPrivateFieldGet(this, _WebServer_db, "f").close();
92
+ }
93
+ catch (error) {
94
+ return reject(error instanceof Error ? error : Error(String(error)));
95
+ }
96
+ }
86
97
  resolve();
87
98
  });
88
99
  }
@@ -98,7 +109,7 @@ export class WebServer {
98
109
  };
99
110
  }
100
111
  }
101
- _WebServer_config = new WeakMap(), _WebServer_app = new WeakMap(), _WebServer_server = new WeakMap(), _WebServer_status = new WeakMap(), _WebServer_port = new WeakMap(), _WebServer_instances = new WeakSet(), _WebServer_getPort = function _WebServer_getPort() {
112
+ _WebServer_config = new WeakMap(), _WebServer_app = new WeakMap(), _WebServer_server = new WeakMap(), _WebServer_status = new WeakMap(), _WebServer_port = new WeakMap(), _WebServer_db = new WeakMap(), _WebServer_instances = new WeakSet(), _WebServer_getPort = function _WebServer_getPort() {
102
113
  if (typeof __classPrivateFieldGet(this, _WebServer_config, "f").port === 'number') {
103
114
  return __classPrivateFieldGet(this, _WebServer_config, "f").port;
104
115
  }
@@ -1 +1 @@
1
- {"version":3,"file":"WebServer.js","sourceRoot":"","sources":["../../../src/browse/server/WebServer.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEhC,OAAO,OAAO,MAAM,UAAU,CAAC;AAE/B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,GAAG,MAAM,iBAAiB,CAAC;AAElC,MAAM,CAAC,MAAM,uBAAuB,GAAG,IAAI,CAAC;AAQ5C,MAAM,OAAO,SAAS;IASpB,YAAY,MAAuB;;QARnC,SAAI,GAAG,WAAW,CAAC;QAEnB,oCAAyB;QACzB,iCAAsB;QACtB,oCAAuB;QACvB,oCAA+B;QAC/B,kCAAqB;QAGnB,uBAAA,IAAI,qBAAW,MAAM,MAAA,CAAC;QACtB,uBAAA,IAAI,kBAAQ,OAAO,EAAE,MAAA,CAAC;QACtB,uBAAA,IAAI,qBAAW,IAAI,MAAA,CAAC;QACpB,uBAAA,IAAI,qBAAW,SAAS,MAAA,CAAC;QACzB,uBAAA,IAAI,mBAAS,MAAM,CAAC,IAAI,IAAI,IAAI,MAAA,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,uBAAA,IAAI,yBAAQ,KAAK,SAAS,EAAE,CAAC;YAC/B,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,uBAAA,IAAI,yBAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QACtD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,MAAM,KAAK,CAAC,mBAAmB,uBAAA,IAAI,yBAAQ,CAAC,OAAO,kBAAkB,CAAC,CAAC;QACzE,CAAC;QACD,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;YACxC,MAAM,KAAK,CAAC,IAAI,uBAAA,IAAI,yBAAQ,CAAC,OAAO,sBAAsB,CAAC,CAAC;QAC9D,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC;QACjE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,MAAM,KAAK,CAAC,YAAY,MAAM,kBAAkB,CAAC,CAAC;QACpD,CAAC;QACD,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,uBAAA,IAAI,yBAAQ,CAAC,MAAM,CAAC,CAAC;QACpE,MAAM,GAAG,GAAG,GAAG,CAAC,WAAW,CAAC,EAAE,EAAE,uBAAA,IAAI,yBAAQ,CAAC,MAAM,CAAC,CAAC;QACrD,MAAM,MAAM,GAAG,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,uBAAA,IAAI,yBAAQ,CAAC,MAAM,CAAC,CAAC;QAEhE,uBAAA,IAAI,sBAAK,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9B,uBAAA,IAAI,sBAAK,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACtD,uBAAA,IAAI,sBAAK,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC;QAC7F,uBAAA,IAAI,sBAAK,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC;QAC7F,uBAAA,IAAI,sBAAK,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC;QAC7F,uBAAA,IAAI,sBAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAEtB,uBAAA,IAAI,mBAAS,MAAM,uBAAA,IAAI,gDAAS,MAAb,IAAI,CAAW,MAAA,CAAC;QAEnC,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC3C,uBAAA,IAAI,qBAAW,uBAAA,IAAI,sBAAK,CAAC,MAAM,CAAC,uBAAA,IAAI,uBAAM,EAAE,CAAC,KAAK,EAAE,EAAE;gBACpD,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,CAAC,KAAK,CAAC,CAAC;oBACd,OAAO;gBACT,CAAC;gBACD,uBAAA,IAAI,qBAAW,SAAS,MAAA,CAAC;gBACzB,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,MAAA,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI;QACF,IAAI,uBAAA,IAAI,yBAAQ,KAAK,SAAS,EAAE,CAAC;YAC/B,OAAO;QACT,CAAC;QACD,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC3C,IAAI,uBAAA,IAAI,yBAAQ,EAAE,CAAC;gBACjB,uBAAA,IAAI,yBAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;oBAC3B,IAAI,KAAK,EAAE,CAAC;wBACV,MAAM,CAAC,KAAK,CAAC,CAAC;wBACd,OAAO;oBACT,CAAC;oBACD,uBAAA,IAAI,qBAAW,IAAI,MAAA,CAAC;oBACpB,uBAAA,IAAI,mBAAS,IAAI,MAAA,CAAC;oBAClB,uBAAA,IAAI,qBAAW,SAAS,MAAA,CAAC;oBACzB,OAAO,EAAE,CAAC;gBACZ,CAAC,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IASD,SAAS;QACP,OAAO;YACL,GAAG,uBAAA,IAAI,yBAAQ;YACf,IAAI,EAAE,uBAAA,IAAI,uBAAM;SACjB,CAAC;IACJ,CAAC;CACF;;IAZG,IAAI,OAAO,uBAAA,IAAI,yBAAQ,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC1C,OAAO,uBAAA,IAAI,yBAAQ,CAAC,IAAI,CAAC;IAC3B,CAAC;IACD,OAAO,OAAO,CAAC,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC,CAAC;AACpD,CAAC","sourcesContent":["import express from 'express';\nimport path from 'path';\nimport fs from 'fs';\nimport DB from '../db/index.js';\nimport { type Server } from 'http';\nimport getPort from 'get-port';\nimport { type Logger } from '../../utils/logging/index.js';\nimport { getRouter } from './Router.js';\nimport API from '../api/index.js';\n\nexport const DEFAULT_WEB_SERVER_PORT = 3000;\n\nexport interface WebServerConfig {\n dataDir?: string;\n port?: number | null;\n logger?: Logger | null;\n}\n\nexport class WebServer {\n name = 'WebServer';\n\n #config: WebServerConfig;\n #app: express.Express;\n #server: Server | null;\n #status: 'stopped' | 'started';\n #port: number | null;\n\n constructor(config: WebServerConfig) {\n this.#config = config;\n this.#app = express();\n this.#server = null;\n this.#status = 'stopped';\n this.#port = config.port || null;\n }\n\n async start() {\n if (this.#status === 'started') {\n return;\n }\n\n const dataDir = this.#config.dataDir || process.cwd();\n if (!fs.existsSync(dataDir)) {\n throw Error(`Data directory \"${this.#config.dataDir}\" does not exist`);\n }\n if (!fs.statSync(dataDir).isDirectory()) {\n throw Error(`\"${this.#config.dataDir}\" is not a directory`);\n }\n\n const dbFile = path.resolve(dataDir, '.patreon-dl', 'db.sqlite');\n if (!fs.existsSync(dbFile)) {\n throw Error(`DB file \"${dbFile}\" does not exist`);\n }\n const db = await DB.getInstance(dbFile, false, this.#config.logger);\n const api = API.getInstance(db, this.#config.logger);\n const router = getRouter(db, api, dataDir, this.#config.logger);\n\n this.#app.use(express.json());\n this.#app.use(express.urlencoded({ extended: true }));\n this.#app.use('/assets', express.static(path.resolve(import.meta.dirname, '../web/assets')));\n this.#app.use('/themes', express.static(path.resolve(import.meta.dirname, '../web/themes')));\n this.#app.use('/images', express.static(path.resolve(import.meta.dirname, '../web/images')));\n this.#app.use(router);\n\n this.#port = await this.#getPort();\n\n return new Promise<void>((resolve, reject) => {\n this.#server = this.#app.listen(this.#port, (error) => {\n if (error) {\n reject(error);\n return;\n }\n this.#status = 'started';\n resolve();\n });\n });\n }\n\n stop() {\n if (this.#status === 'stopped') {\n return;\n }\n return new Promise<void>((resolve, reject) => {\n if (this.#server) {\n this.#server.close((error) => {\n if (error) {\n reject(error);\n return;\n }\n this.#server = null;\n this.#port = null;\n this.#status = 'stopped';\n resolve();\n });\n } else {\n resolve();\n }\n });\n }\n\n #getPort() {\n if (typeof this.#config.port === 'number') {\n return this.#config.port;\n }\n return getPort({ port: DEFAULT_WEB_SERVER_PORT });\n }\n\n getConfig(): WebServerConfig {\n return {\n ...this.#config,\n port: this.#port\n };\n }\n}\n"]}
1
+ {"version":3,"file":"WebServer.js","sourceRoot":"","sources":["../../../src/browse/server/WebServer.ts"],"names":[],"mappings":"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","sourcesContent":["import express from 'express';\nimport path from 'path';\nimport fs from 'fs';\nimport DB, { type DBInstance } from '../db/index.js';\nimport { type Server } from 'http';\nimport getPort from 'get-port';\nimport { type Logger } from '../../utils/logging/index.js';\nimport { getRouter } from './Router.js';\nimport API from '../api/index.js';\n\nexport const DEFAULT_WEB_SERVER_PORT = 3000;\n\nexport interface WebServerConfig {\n dataDir?: string;\n port?: number | null;\n logger?: Logger | null;\n}\n\nexport class WebServer {\n name = 'WebServer';\n\n #config: WebServerConfig;\n #app: express.Express;\n #server: Server | null;\n #status: 'stopped' | 'started';\n #port: number | null;\n\n #db: DBInstance | null;\n\n constructor(config: WebServerConfig) {\n this.#config = config;\n this.#app = express();\n this.#server = null;\n this.#status = 'stopped';\n this.#port = config.port || null;\n this.#db = null;\n }\n\n async start() {\n if (this.#status === 'started') {\n return;\n }\n\n const dataDir = this.#config.dataDir || process.cwd();\n if (!fs.existsSync(dataDir)) {\n throw Error(`Data directory \"${this.#config.dataDir}\" does not exist`);\n }\n if (!fs.statSync(dataDir).isDirectory()) {\n throw Error(`\"${this.#config.dataDir}\" is not a directory`);\n }\n\n const dbFile = path.resolve(dataDir, '.patreon-dl', 'db.sqlite');\n if (!fs.existsSync(dbFile)) {\n throw Error(`DB file \"${dbFile}\" does not exist`);\n }\n const db = await DB.getInstance(dbFile, false, this.#config.logger);\n const api = API.getInstance(db, this.#config.logger);\n const router = getRouter(db, api, dataDir, this.#config.logger);\n\n this.#app.use(express.json());\n this.#app.use(express.urlencoded({ extended: true }));\n this.#app.use('/assets', express.static(path.resolve(import.meta.dirname, '../web/assets')));\n this.#app.use('/themes', express.static(path.resolve(import.meta.dirname, '../web/themes')));\n this.#app.use('/images', express.static(path.resolve(import.meta.dirname, '../web/images')));\n this.#app.use(router);\n\n this.#db = db;\n this.#port = await this.#getPort();\n\n return new Promise<void>((resolve, reject) => {\n this.#server = this.#app.listen(this.#port, (error) => {\n if (error) {\n reject(error);\n return;\n }\n this.#status = 'started';\n resolve();\n });\n });\n }\n\n stop() {\n if (this.#status === 'stopped') {\n return;\n }\n return new Promise<void>((resolve, reject) => {\n if (this.#server) {\n this.#server.close((error) => {\n if (error) {\n return reject(error);;\n }\n this.#server = null;\n this.#port = null;\n this.#status = 'stopped';\n if (this.#db) {\n try {\n this.#db.close();\n }\n catch (error) {\n return reject(error instanceof Error ? error : Error(String(error)));\n }\n }\n resolve();\n });\n } else {\n resolve();\n }\n });\n }\n\n #getPort() {\n if (typeof this.#config.port === 'number') {\n return this.#config.port;\n }\n return getPort({ port: DEFAULT_WEB_SERVER_PORT });\n }\n\n getConfig(): WebServerConfig {\n return {\n ...this.#config,\n port: this.#port\n };\n }\n}\n"]}
@@ -78,6 +78,7 @@ export default abstract class Downloader<T extends DownloaderType> extends Event
78
78
  json: any;
79
79
  error: any;
80
80
  }>;
81
+ protected closeDB(): Promise<void>;
81
82
  on<T extends DownloaderEvent>(event: T, listener: (args: DownloaderEventPayloadOf<T>) => void): this;
82
83
  once<T extends DownloaderEvent>(event: T, listener: (args: DownloaderEventPayloadOf<T>) => void): this;
83
84
  off<T extends DownloaderEvent>(event: T, listener: (args: DownloaderEventPayloadOf<T>) => void): this;
@@ -410,6 +410,12 @@ class Downloader extends EventEmitter {
410
410
  this.log('debug', `Fetch user data from API URL "${url}"`);
411
411
  return this.commonFetchAPI(url, signal);
412
412
  }
413
+ async closeDB() {
414
+ if (__classPrivateFieldGet(this, _Downloader_dbPromise, "f")) {
415
+ (await __classPrivateFieldGet(this, _Downloader_dbPromise, "f")).close();
416
+ __classPrivateFieldSet(this, _Downloader_dbPromise, null, "f");
417
+ }
418
+ }
413
419
  on(event, listener) {
414
420
  return super.on(event, listener);
415
421
  }