forked from coder/code-server
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstorageDatabase.ts
More file actions
129 lines (105 loc) · 3.49 KB
/
storageDatabase.ts
File metadata and controls
129 lines (105 loc) · 3.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import { readFile, writeFile } from "fs";
import { mkdirp } from "fs-extra";
import * as path from "path";
import { promisify } from "util";
import { IDisposable } from "@coder/disposable";
import { logger, field } from "@coder/logger";
import { Event } from "vs/base/common/event";
import * as workspaceStorage from "vs/base/node/storage";
import * as globalStorage from "vs/platform/storage/node/storageIpc";
import { IStorageService, WillSaveStateReason } from "vs/platform/storage/common/storage";
import * as paths from "./paths";
import { workbench } from "../workbench";
class StorageDatabase implements workspaceStorage.IStorageDatabase {
public readonly onDidChangeItemsExternal = Event.None;
private readonly items = new Map<string, string>();
private fetched: boolean = false;
private readonly path: string;
public constructor(path: string) {
this.path = path.replace(/\.vscdb$/, ".json");
logger.debug("Setting up storage", field("path", this.path));
window.addEventListener("unload", () => {
if (!navigator.sendBeacon) {
throw new Error("cannot save state");
}
this.triggerFlush(WillSaveStateReason.SHUTDOWN);
navigator.sendBeacon(`/resource${this.path}`, this.content);
});
}
public async getItems(): Promise<Map<string, string>> {
if (this.fetched) {
return this.items;
}
try {
const contents = await promisify(readFile)(this.path, "utf8");
const json = JSON.parse(contents);
Object.keys(json).forEach((key) => {
this.items.set(key, json[key]);
});
} catch (error) {
if (error.code !== "ENOENT") {
throw error;
}
}
this.fetched = true;
return this.items;
}
public updateItems(request: workspaceStorage.IUpdateRequest): Promise<void> {
if (request.insert) {
request.insert.forEach((value, key) => {
if (key === "colorThemeData") {
localStorage.setItem("colorThemeData", value);
}
this.items.set(key, value);
});
}
if (request.delete) {
request.delete.forEach(key => this.items.delete(key));
}
return this.save();
}
public close(): Promise<void> {
return Promise.resolve();
}
public checkIntegrity(): Promise<string> {
return Promise.resolve("ok");
}
private async save(): Promise<void> {
await mkdirp(path.dirname(this.path));
return promisify(writeFile)(this.path, this.content);
}
private triggerFlush(reason: WillSaveStateReason = WillSaveStateReason.NONE): boolean {
// tslint:disable-next-line:no-any
const storageService = workbench.serviceCollection.get<IStorageService>(IStorageService) as any;
if (reason === WillSaveStateReason.SHUTDOWN && storageService.close) {
storageService.close();
return true;
}
if (storageService._onWillSaveState) {
storageService._onWillSaveState.fire({ reason });
return true;
}
return false;
}
private get content(): string {
const json: { [key: string]: string } = {};
this.items.forEach((value, key) => {
json[key] = value;
});
return JSON.stringify(json);
}
}
class GlobalStorageDatabase extends StorageDatabase implements IDisposable {
public constructor() {
super(path.join(paths.getAppDataPath(), "globalStorage", "state.vscdb"));
}
public dispose(): void {
// Nothing to do.
}
}
const workspaceTarget = workspaceStorage as typeof workspaceStorage;
// @ts-ignore TODO: don't ignore it.
workspaceTarget.SQLiteStorageDatabase = StorageDatabase;
const globalTarget = globalStorage as typeof globalStorage;
// @ts-ignore TODO: don't ignore it.
globalTarget.GlobalStorageDatabaseChannelClient = GlobalStorageDatabase;