@wirechunk/cli 0.0.6 → 0.0.8
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/build/main.js +27191 -22361
- package/package.json +14 -8
- package/src/commands/bootstrap.ts +0 -85
- package/src/commands/create-extension-version.ts +0 -184
- package/src/commands/create-extension.ts +0 -14
- package/src/commands/create-user.ts +0 -211
- package/src/commands/edit-admin.ts +0 -139
- package/src/commands/ext-dev/get-db-url.ts +0 -36
- package/src/commands/ext-dev/init-db.ts +0 -228
- package/src/core-api/api.ts +0 -4807
- package/src/core-api/mutations/create-extension-version.generated.ts +0 -96
- package/src/core-api/mutations/create-extension-version.graphql +0 -14
- package/src/core-api/operations.ts +0 -23
- package/src/env.ts +0 -85
- package/src/errors.ts +0 -30
- package/src/global-options.ts +0 -6
- package/src/main.ts +0 -136
- package/src/users/permissions.ts +0 -41
- package/src/util.ts +0 -81
- package/tsconfig.build.json +0 -10
- package/tsconfig.json +0 -26
- package/vite.config.ts +0 -18
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import { readFile, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { resolve } from 'node:path';
|
|
4
|
-
import type { Env } from '../../env.ts';
|
|
5
|
-
import type { WithGlobalOptions } from '../../global-options.js';
|
|
6
|
-
import { getExtensionDbConnectInfo, replaceEnvVar } from '../../util.ts';
|
|
7
|
-
|
|
8
|
-
type GetDbUrlOptions = {
|
|
9
|
-
extensionId?: string;
|
|
10
|
-
dbName?: string;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export const getDbUrl = async (
|
|
14
|
-
opts: WithGlobalOptions<GetDbUrlOptions>,
|
|
15
|
-
env: Env,
|
|
16
|
-
): Promise<void> => {
|
|
17
|
-
const connInfo = getExtensionDbConnectInfo(opts, env);
|
|
18
|
-
const url = connInfo.url.toString();
|
|
19
|
-
const cwd = process.cwd();
|
|
20
|
-
const localEnvFilePath = opts.envMode
|
|
21
|
-
? resolve(cwd, `./.env.${opts.envMode}.local`)
|
|
22
|
-
: resolve(cwd, './.env.local');
|
|
23
|
-
if (opts.verbose) {
|
|
24
|
-
console.log(`Loading file ${localEnvFilePath} to update environment variables`);
|
|
25
|
-
}
|
|
26
|
-
if (existsSync(localEnvFilePath)) {
|
|
27
|
-
const localEnv = await readFile(localEnvFilePath, 'utf8');
|
|
28
|
-
const lines = replaceEnvVar(localEnv.split('\n'), 'DATABASE_URL', url);
|
|
29
|
-
await writeFile(localEnvFilePath, lines.join('\n'));
|
|
30
|
-
} else {
|
|
31
|
-
if (opts.verbose) {
|
|
32
|
-
console.log(`Creating file ${localEnvFilePath}`);
|
|
33
|
-
}
|
|
34
|
-
await writeFile(localEnvFilePath, `DATABASE_URL=${url}\n`);
|
|
35
|
-
}
|
|
36
|
-
};
|
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
import type { DatabasePool } from 'slonik';
|
|
2
|
-
import { createPool, sql } from 'slonik';
|
|
3
|
-
import { z } from 'zod';
|
|
4
|
-
import type { Env } from '../../env.js';
|
|
5
|
-
import { requireCoreDbUrl } from '../../env.ts';
|
|
6
|
-
import { isDuplicateDatabaseError } from '../../errors.ts';
|
|
7
|
-
import type { WithGlobalOptions } from '../../global-options.js';
|
|
8
|
-
import {
|
|
9
|
-
dbPoolOptions,
|
|
10
|
-
getExtensionDbConnectInfo,
|
|
11
|
-
requireExtensionIdOptionOrEnvVar,
|
|
12
|
-
} from '../../util.ts';
|
|
13
|
-
import { getDbUrl } from './get-db-url.ts';
|
|
14
|
-
|
|
15
|
-
const initExtDb = async ({
|
|
16
|
-
// A connection to the extension database by a superuser role.
|
|
17
|
-
db,
|
|
18
|
-
extensionRoleName,
|
|
19
|
-
coreDbUrl,
|
|
20
|
-
}: {
|
|
21
|
-
db: DatabasePool;
|
|
22
|
-
extensionRoleName: string;
|
|
23
|
-
coreDbUrl: URL;
|
|
24
|
-
}) => {
|
|
25
|
-
await db.query(sql.unsafe`
|
|
26
|
-
create extension if not exists postgres_fdw
|
|
27
|
-
`);
|
|
28
|
-
await db.query(sql.unsafe`
|
|
29
|
-
create server if not exists wirechunk
|
|
30
|
-
foreign data wrapper postgres_fdw
|
|
31
|
-
options (
|
|
32
|
-
host ${sql.literalValue(coreDbUrl.hostname)},
|
|
33
|
-
port ${sql.literalValue(coreDbUrl.port)},
|
|
34
|
-
dbname ${sql.literalValue(coreDbUrl.pathname.slice(1))}
|
|
35
|
-
)
|
|
36
|
-
`);
|
|
37
|
-
|
|
38
|
-
const extRoleIdent = sql.identifier([extensionRoleName]);
|
|
39
|
-
|
|
40
|
-
await db.query(sql.unsafe`
|
|
41
|
-
create user mapping if not exists for ${extRoleIdent} server wirechunk
|
|
42
|
-
options (user ${sql.literalValue(extensionRoleName)}, password_required 'false')
|
|
43
|
-
`);
|
|
44
|
-
await db.query(sql.unsafe`
|
|
45
|
-
create foreign table if not exists "Users" (
|
|
46
|
-
"id" text not null,
|
|
47
|
-
"firstName" text not null,
|
|
48
|
-
"lastName" text not null,
|
|
49
|
-
"email" text not null,
|
|
50
|
-
"emailVerified" boolean not null,
|
|
51
|
-
"orgId" text,
|
|
52
|
-
"role" text,
|
|
53
|
-
"status" text not null,
|
|
54
|
-
"expiresAt" timestamptz,
|
|
55
|
-
"createdAt" timestamptz not null
|
|
56
|
-
) server wirechunk options (schema_name 'public', table_name 'Users')
|
|
57
|
-
`);
|
|
58
|
-
await db.query(sql.unsafe`
|
|
59
|
-
create foreign table if not exists "Orgs" (
|
|
60
|
-
"id" text not null,
|
|
61
|
-
"name" text,
|
|
62
|
-
"primaryUserId" text,
|
|
63
|
-
"createdAt" timestamptz not null
|
|
64
|
-
) server wirechunk options (schema_name 'public', table_name 'Orgs')
|
|
65
|
-
`);
|
|
66
|
-
await db.query(sql.unsafe`
|
|
67
|
-
create foreign table if not exists "Sites" (
|
|
68
|
-
"id" text not null,
|
|
69
|
-
"domain" text not null,
|
|
70
|
-
"orgId" text,
|
|
71
|
-
"name" text not null,
|
|
72
|
-
"createdAt" timestamptz not null
|
|
73
|
-
) server wirechunk options (schema_name 'public', table_name 'Sites')
|
|
74
|
-
`);
|
|
75
|
-
|
|
76
|
-
// These are not the permissions granted to actual extension roles in production.
|
|
77
|
-
// Here we just want to simplify the development experience.
|
|
78
|
-
// At any moment, a developer may drop an extension's database and reinitialize it.
|
|
79
|
-
await db.query(sql.unsafe`
|
|
80
|
-
grant all on schema public to ${extRoleIdent}
|
|
81
|
-
`);
|
|
82
|
-
await db.query(sql.unsafe`
|
|
83
|
-
grant all on table "Orgs" to ${extRoleIdent}
|
|
84
|
-
`);
|
|
85
|
-
await db.query(sql.unsafe`
|
|
86
|
-
grant all on table "Sites" to ${extRoleIdent}
|
|
87
|
-
`);
|
|
88
|
-
await db.query(sql.unsafe`
|
|
89
|
-
grant all on table "Users" to ${extRoleIdent}
|
|
90
|
-
`);
|
|
91
|
-
await db.query(sql.unsafe`
|
|
92
|
-
alter default privileges grant all on schemas to ${extRoleIdent}
|
|
93
|
-
`);
|
|
94
|
-
await db.query(sql.unsafe`
|
|
95
|
-
alter default privileges grant all on types to ${extRoleIdent}
|
|
96
|
-
`);
|
|
97
|
-
await db.query(sql.unsafe`
|
|
98
|
-
alter default privileges grant all on tables to ${extRoleIdent}
|
|
99
|
-
`);
|
|
100
|
-
await db.query(sql.unsafe`
|
|
101
|
-
alter default privileges grant all on sequences to ${extRoleIdent}
|
|
102
|
-
`);
|
|
103
|
-
await db.query(sql.unsafe`
|
|
104
|
-
alter default privileges grant all on functions to ${extRoleIdent}
|
|
105
|
-
`);
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
const extensionSelectSchema = z.object({
|
|
109
|
-
id: z.string(),
|
|
110
|
-
platformId: z.string(),
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
const applyExtensionPolicy = async ({
|
|
114
|
-
extRole,
|
|
115
|
-
table,
|
|
116
|
-
platformId,
|
|
117
|
-
db,
|
|
118
|
-
}: {
|
|
119
|
-
extRole: string;
|
|
120
|
-
table: string;
|
|
121
|
-
platformId: string;
|
|
122
|
-
db: DatabasePool;
|
|
123
|
-
}) => {
|
|
124
|
-
const policyNameIdent = sql.identifier([`${table}_${extRole}_select`]);
|
|
125
|
-
const extRoleIdent = sql.identifier([extRole]);
|
|
126
|
-
const tableIdent = sql.identifier([table]);
|
|
127
|
-
await db.query(sql.unsafe`
|
|
128
|
-
drop policy if exists ${policyNameIdent} on ${tableIdent}
|
|
129
|
-
`);
|
|
130
|
-
await db.query(sql.unsafe`
|
|
131
|
-
create policy ${policyNameIdent} on ${tableIdent} as permissive
|
|
132
|
-
for select to ${extRoleIdent}
|
|
133
|
-
using ("platformId" = ${sql.literalValue(platformId)})
|
|
134
|
-
`);
|
|
135
|
-
await db.query(sql.unsafe`
|
|
136
|
-
grant select on table ${tableIdent} to ${extRoleIdent}
|
|
137
|
-
`);
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
type InitExtDbOptions = {
|
|
141
|
-
extensionId?: string;
|
|
142
|
-
dbName?: string;
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
export const initDb = async (
|
|
146
|
-
opts: WithGlobalOptions<InitExtDbOptions>,
|
|
147
|
-
env: Env,
|
|
148
|
-
): Promise<void> => {
|
|
149
|
-
const coreDbUrl = requireCoreDbUrl(env);
|
|
150
|
-
const extensionId = requireExtensionIdOptionOrEnvVar(opts, env);
|
|
151
|
-
|
|
152
|
-
// This writes the DATABASE_URL environment variable to a .env.local (or .env.<env-mode>.local) file.
|
|
153
|
-
await getDbUrl(opts, env);
|
|
154
|
-
|
|
155
|
-
const db = await createPool(coreDbUrl, dbPoolOptions(opts));
|
|
156
|
-
|
|
157
|
-
const extension = await db.maybeOne(sql.type(extensionSelectSchema)`
|
|
158
|
-
select "id", "platformId"
|
|
159
|
-
from "Extensions"
|
|
160
|
-
where "id" = ${extensionId}
|
|
161
|
-
`);
|
|
162
|
-
if (!extension) {
|
|
163
|
-
console.error(`Extension with ID ${extensionId} not found`);
|
|
164
|
-
process.exit(1);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const coreDbUrlObject = new URL(coreDbUrl);
|
|
168
|
-
const extDb = getExtensionDbConnectInfo(opts, env);
|
|
169
|
-
|
|
170
|
-
try {
|
|
171
|
-
await db.query(sql.unsafe`
|
|
172
|
-
create database ${sql.identifier([extDb.dbName])}
|
|
173
|
-
`);
|
|
174
|
-
} catch (err) {
|
|
175
|
-
if (!isDuplicateDatabaseError(err)) {
|
|
176
|
-
console.error('Failed to create a database:', err);
|
|
177
|
-
process.exit(1);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const extRole = `ext_${extension.id}`;
|
|
182
|
-
|
|
183
|
-
const extRoleIdent = sql.identifier([extRole]);
|
|
184
|
-
await db.query(sql.unsafe`
|
|
185
|
-
do $$
|
|
186
|
-
begin
|
|
187
|
-
if not exists (
|
|
188
|
-
select 1 from pg_roles where rolname = ${sql.literalValue(extRole)}
|
|
189
|
-
) then
|
|
190
|
-
create role ${extRoleIdent} login noinherit createdb;
|
|
191
|
-
end if;
|
|
192
|
-
end; $$
|
|
193
|
-
`);
|
|
194
|
-
await db.query(sql.unsafe`
|
|
195
|
-
grant ${extRoleIdent} to ${sql.identifier([coreDbUrlObject.username])}
|
|
196
|
-
`);
|
|
197
|
-
|
|
198
|
-
await db.query(sql.unsafe`
|
|
199
|
-
grant connect, create, temporary on database ${sql.identifier([extDb.dbName])} to ${extRoleIdent}
|
|
200
|
-
`);
|
|
201
|
-
await applyExtensionPolicy({
|
|
202
|
-
extRole,
|
|
203
|
-
table: 'Orgs',
|
|
204
|
-
platformId: extension.platformId,
|
|
205
|
-
db,
|
|
206
|
-
});
|
|
207
|
-
await applyExtensionPolicy({
|
|
208
|
-
extRole,
|
|
209
|
-
table: 'Sites',
|
|
210
|
-
platformId: extension.platformId,
|
|
211
|
-
db,
|
|
212
|
-
});
|
|
213
|
-
await applyExtensionPolicy({
|
|
214
|
-
extRole,
|
|
215
|
-
table: 'Users',
|
|
216
|
-
platformId: extension.platformId,
|
|
217
|
-
db,
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
const extDbSuper = new URL(extDb.url);
|
|
221
|
-
extDbSuper.username = coreDbUrlObject.username;
|
|
222
|
-
|
|
223
|
-
await initExtDb({
|
|
224
|
-
db: await createPool(extDbSuper.toString(), dbPoolOptions(opts)),
|
|
225
|
-
extensionRoleName: extRole,
|
|
226
|
-
coreDbUrl: coreDbUrlObject,
|
|
227
|
-
});
|
|
228
|
-
};
|