Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/conat/persist/util.ts
1710 views
1
/*
2
3
Maybe storage available as a service.
4
5
This code is similar to the changefeed server, because
6
it provides a changefeed on a given persist storage,
7
and a way to see values.
8
9
DEVELOPMENT:
10
11
Change to the packages/backend directory and run node.
12
13
TERMINAL 1: This sets up the environment and starts the server running:
14
15
require('@cocalc/backend/conat/persist').initServer()
16
17
18
TERMINAL 2: In another node session, create a client:
19
20
user = {account_id:'00000000-0000-4000-8000-000000000000'}; storage = {path:'a.db'}; const {id, stream} = await require('@cocalc/backend/conat/persist').getAll({user, storage}); console.log({id}); for await(const x of stream) { console.log(x.data) }; console.log("DONE")
21
22
// client also does this periodically to keep subscription alive:
23
24
await renew({user, id })
25
26
TERMINAL 3:
27
28
user = {account_id:'00000000-0000-4000-8000-000000000000'}; storage = {path:'a.db'}; const {set,get} = require('@cocalc/backend/conat/persist'); const { messageData } =require("@cocalc/conat/core/client"); 0;
29
30
await set({user, storage, messageData:messageData('hi')})
31
32
await get({user, storage, seq:1})
33
34
await set({user, storage, key:'bella', messageData:messageData('hi', {headers:{x:10}})})
35
36
await get({user, storage, key:'bella'})
37
38
Also getAll using start_seq:
39
40
cf = const {id, stream} = await require('@cocalc/backend/conat/persist').getAll({user, storage, start_seq:10}); for await(const x of stream) { console.log(x) };
41
*/
42
43
import { assertHasWritePermission } from "./auth";
44
import { pstream, PersistentStream } from "./storage";
45
import { join } from "path";
46
import { syncFiles, ensureContainingDirectoryExists } from "./context";
47
48
// this is per-server -- and "user" means where the resource is, usually
49
// a given project. E.g., 500 streams in a project, across many users.
50
export const MAX_PER_USER = 500;
51
export const MAX_GLOBAL = 10000;
52
export const RESOURCE = "persistent storage";
53
54
//import { getLogger } from "@cocalc/conat/client";
55
//const logger = getLogger("persist:util");
56
57
export const SERVICE = "persist";
58
59
export type User = {
60
account_id?: string;
61
project_id?: string;
62
hub_id?: string;
63
};
64
65
export function persistSubject({
66
account_id,
67
project_id,
68
service = SERVICE,
69
}: User & { service?: string }) {
70
if (account_id) {
71
return `${service}.account-${account_id}`;
72
} else if (project_id) {
73
return `${service}.project-${project_id}`;
74
} else {
75
return `${service}.hub`;
76
}
77
}
78
79
export async function getStream({
80
subject,
81
storage,
82
service,
83
}): Promise<PersistentStream> {
84
// this permsissions check should always work and use should
85
// never see an error here, since
86
// the same check runs on the client before the message is sent to the
87
// persist storage. However a malicious user would hit this.
88
// IMPORTANT: must be here so error *code* also sent back.
89
assertHasWritePermission({
90
subject,
91
path: storage.path,
92
service,
93
});
94
const path = join(syncFiles.local, storage.path);
95
const archive = syncFiles.archive
96
? join(syncFiles.archive, storage.path)
97
: undefined;
98
const backup = syncFiles.backup
99
? join(syncFiles.backup, storage.path)
100
: undefined;
101
const archiveInterval = syncFiles.archiveInterval;
102
await Promise.all(
103
[path, archive, backup]
104
.filter((x) => x)
105
.map(ensureContainingDirectoryExists),
106
);
107
return pstream({ ...storage, path, archive, backup, archiveInterval });
108
}
109
110