Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/sync-client/lib/index.ts
1503 views
1
/*
2
This is specifically meant for connecting to one project.
3
4
Example use:
5
6
~/cocalc/src/packages/sync-client$ PROJECT_PORT=33177 DEBUG='cocalc:sync*' node
7
...
8
9
> c = new (require('.').default)(); s = c.sync_client.sync_db({project_id:'97ce5a7c-25c1-4059-8670-c7de96a0db92',path:'b.tasks',primary_keys:["task_id"], string_cols:['desc']})
10
11
> s.set({task_id:'cf163fb4-b198-4664-b32b-82ce4ec71701',desc:"fubar"})
12
> await s.save()
13
> s.to_str()
14
'{"desc":"fubar","last_edited":1684420716277,"position":0,"task_id":"cf163fb4-b198-4664-b32b-82ce4ec71701"}'
15
> s.set({task_id:'cf163fb4-b198-4664-b32b-82ce4ec71701',desc:"figure it out"})
16
undefined
17
> await s.save()
18
undefined
19
> s.to_str()
20
'{"desc":"figure it out","last_edited":1684420716277,"position":0,"task_id":"cf163fb4-b198-4664-b32b-82ce4ec71701"}'
21
*/
22
23
import { EventEmitter } from "events";
24
import type { AppClient } from "@cocalc/sync/client/types";
25
import { SyncClient } from "@cocalc/sync/client/sync-client";
26
import ProjectClient from "./project-client";
27
import debug from "debug";
28
import { bind_methods, isValidUUID, uuid } from "@cocalc/util/misc";
29
import { project } from "@cocalc/api-client";
30
import { reuseInFlight } from "@cocalc/util/reuse-in-flight";
31
32
export type Role = "project" | "browser" | "compute_server";
33
34
interface Options {
35
project_id: string;
36
client_id?: string;
37
role: Role;
38
}
39
40
export default class Client extends EventEmitter implements AppClient {
41
project_client: ProjectClient;
42
sync_client: SyncClient;
43
synctable_project: Function;
44
project_id: string;
45
_client_id: string;
46
private role: Role;
47
48
constructor({ project_id, client_id = uuid(), role }: Options) {
49
super();
50
this._client_id = client_id;
51
this.project_id = project_id;
52
this.role = role;
53
54
if (!isValidUUID(project_id)) {
55
throw Error("project_id must be a valid uuid");
56
}
57
58
this.project_client = bind_methods(new ProjectClient());
59
this.sync_client = bind_methods(new SyncClient(this));
60
}
61
62
client_id = () => {
63
return this._client_id;
64
};
65
66
// [ ] TODO: is this something we should worry about? Probably yes.
67
is_deleted = (_filename: string, _project_id: string) => {
68
return false;
69
};
70
71
set_deleted = async (_filename: string, _project_id?: string) => {
72
// TODO -- implement in fs aware clients
73
};
74
75
mark_file = async (_opts: any) => {
76
// [ ] TODO: should we?
77
};
78
79
is_project = () => {
80
return this.role == "project";
81
};
82
83
is_browser = () => {
84
return this.role == "browser";
85
};
86
87
is_compute_server = () => {
88
return this.role == "compute_server";
89
};
90
91
dbg = (str: string) => {
92
return debug(`cocalc:sync:client.${str}`);
93
};
94
95
query = (opts) => {
96
this.dbg("query")(opts);
97
if (typeof opts?.query != "object") {
98
throw Error("opts.query must be specified");
99
}
100
let project_id = this.project_id;
101
for (const table in opts.query) {
102
if (opts.query[table].project_id) {
103
project_id = opts.query[table].project_id;
104
break;
105
}
106
if (opts.query[table][0]?.project_id) {
107
project_id = opts.query[table][0]?.project_id;
108
break;
109
}
110
}
111
if (!project_id) {
112
throw Error(
113
"query involving an explicit project_id or clients with project_id set are supported",
114
);
115
}
116
(async () => {
117
try {
118
const api = await this.project_client.api(project_id);
119
const result = await api.query(opts);
120
opts.cb?.(undefined, result);
121
} catch (err) {
122
opts.cb?.(`${err}`);
123
}
124
})();
125
};
126
127
query_cancel = () => {
128
console.log("query_cancel");
129
};
130
131
server_time = () => {
132
return new Date();
133
};
134
135
is_connected = () => {
136
return true;
137
};
138
139
is_signed_in = () => {
140
return true;
141
};
142
143
private _touchProject = reuseInFlight(async (project_id: string) => {
144
const dbg = this.dbg("sync-client:_touchProject");
145
dbg(project_id);
146
try {
147
await project.touch({ project_id });
148
} catch (err) {
149
dbg("error ", err);
150
}
151
});
152
153
touch_project = (project_id: string, _compute_server_id?: number) => {
154
this._touchProject(project_id);
155
};
156
}
157
158