1 | import fs from 'fs';
|
2 | import lockFile from 'lockfile';
|
3 | import logger from '../core/logger';
|
4 |
|
5 | export default class LockFileInstance {
|
6 | private filePath: string;
|
7 | private lockKey: string;
|
8 | private logger: any;
|
9 | private tryMax: number;
|
10 | private tryGap: number;
|
11 | private tryCount: number;
|
12 |
|
13 | constructor(filePath: string, lockKey: string) {
|
14 | this.filePath = filePath;
|
15 | this.lockKey = lockKey;
|
16 | this.logger = logger({
|
17 | debug: false,
|
18 | silent: true
|
19 | });
|
20 | this.tryMax = 50;
|
21 | this.tryGap = 100;
|
22 | this.tryCount = 0;
|
23 | }
|
24 |
|
25 | clearFile(): void {
|
26 | fs.writeFileSync(this.filePath, JSON.stringify({}, null, 2), 'utf-8');
|
27 | }
|
28 |
|
29 | checkIfCanRead(cb: any): void {
|
30 | lockFile.check(this.lockKey, (err, status) => {
|
31 | if (err) return this.logger.error(err);
|
32 | if (status) {
|
33 |
|
34 | if (this.tryCount >= this.tryMax) {
|
35 | this.tryCount = 0;
|
36 | return this.logger.error(`file read time out ${this.filePath}`);
|
37 | }
|
38 | this.tryCount++;
|
39 | setTimeout(() => {
|
40 | this.checkIfCanRead(cb);
|
41 | }, this.tryGap);
|
42 | } else {
|
43 |
|
44 | this.tryCount = 0;
|
45 | cb && cb();
|
46 | }
|
47 | });
|
48 | }
|
49 |
|
50 | read(key: string | undefined): Promise<any> {
|
51 | return new Promise(resolve => {
|
52 | try {
|
53 | fs.stat(
|
54 | this.filePath,
|
55 | (err: Error | null, stats: { isFile: () => boolean }) => {
|
56 | if (err) {
|
57 | this.logger.error(err);
|
58 | resolve(undefined);
|
59 | return;
|
60 | }
|
61 | if (!stats) {
|
62 | this.logger.error('no stats');
|
63 | resolve(undefined);
|
64 | return;
|
65 | }
|
66 | if (stats && !stats.isFile()) {
|
67 | resolve(undefined);
|
68 | return;
|
69 | }
|
70 |
|
71 | this.checkIfCanRead(() => {
|
72 | fs.readFile(
|
73 | this.filePath,
|
74 | 'utf8',
|
75 | (err: Error | null, data: string) => {
|
76 | if (err) {
|
77 | this.logger.error(err);
|
78 | resolve(undefined);
|
79 | return;
|
80 | }
|
81 |
|
82 | if (!data) {
|
83 |
|
84 | this.clearFile();
|
85 | data = '{}';
|
86 | }
|
87 |
|
88 | this.logger.debug(
|
89 | `get file: ${this.filePath} => data: ${data}`
|
90 | );
|
91 | try {
|
92 | const jsonObj: object = JSON.parse(data);
|
93 | if (key && !jsonObj[key]) {
|
94 | this.logger.debug(
|
95 | `get key ${key} form data ${this.filePath} => no value find`
|
96 | );
|
97 | resolve(undefined);
|
98 | return;
|
99 | }
|
100 | resolve(key ? jsonObj[key] : jsonObj);
|
101 | } catch (e) {
|
102 |
|
103 | this.clearFile();
|
104 | this.logger.error(e);
|
105 | resolve(undefined);
|
106 | }
|
107 | }
|
108 | );
|
109 | });
|
110 | }
|
111 | );
|
112 | } catch (err) {
|
113 | this.logger.error(err);
|
114 | resolve(undefined);
|
115 | }
|
116 | });
|
117 | }
|
118 |
|
119 | lock(cb: any): void {
|
120 | lockFile.lock(
|
121 | this.lockKey,
|
122 | {
|
123 | wait: this.tryGap,
|
124 | retries: this.tryMax
|
125 | },
|
126 | err => {
|
127 | if (err) {
|
128 | this.logger.error(err);
|
129 | this.unlock();
|
130 | cb && cb(err);
|
131 | return;
|
132 | }
|
133 |
|
134 | cb && cb();
|
135 | }
|
136 | );
|
137 | }
|
138 |
|
139 | unlock(): void {
|
140 | lockFile.unlock(this.lockKey, er => {
|
141 | er && this.logger.error('er', er);
|
142 | });
|
143 | }
|
144 |
|
145 | update(key: string, value: any): Promise<object | undefined> {
|
146 | return new Promise(resolve => {
|
147 | try {
|
148 | if (!fs.existsSync(this.filePath)) {
|
149 |
|
150 | this.clearFile();
|
151 | }
|
152 | if (!key || Object.prototype.toString.call(key) !== '[object String]') {
|
153 | this.logger.error(`write file ${this.filePath} key not valid ${key}`);
|
154 | resolve(undefined);
|
155 | return;
|
156 | }
|
157 | this.lock((err: Error | undefined | null) => {
|
158 | if (err) {
|
159 | this.unlock();
|
160 | resolve(undefined);
|
161 | return;
|
162 | }
|
163 |
|
164 | let data = fs.readFileSync(this.filePath, 'utf-8');
|
165 | if (!data) {
|
166 |
|
167 | this.clearFile();
|
168 | data = '{}';
|
169 | }
|
170 | try {
|
171 | const jsonObj: object = JSON.parse(data);
|
172 | this.logger.debug(
|
173 | `write file ${this.filePath} key ${key} value ${value}`
|
174 | );
|
175 | jsonObj[key] = value;
|
176 | fs.writeFile(
|
177 | this.filePath,
|
178 | JSON.stringify(jsonObj, null, 2),
|
179 | (err: Error | null) => {
|
180 | this.unlock();
|
181 | if (err) {
|
182 | this.logger.error(err);
|
183 | resolve(undefined);
|
184 | return;
|
185 | }
|
186 | resolve(jsonObj);
|
187 | }
|
188 | );
|
189 | } catch (e) {
|
190 |
|
191 | this.clearFile();
|
192 | this.logger.error(e);
|
193 | this.unlock();
|
194 | resolve(undefined);
|
195 | }
|
196 | });
|
197 | } catch (err) {
|
198 | this.logger.error(err);
|
199 | this.unlock();
|
200 | resolve(undefined);
|
201 | }
|
202 | });
|
203 | }
|
204 | }
|