@hot-updater/supabase 0.21.10 → 0.21.12
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/dist/index.cjs +129 -125
- package/dist/index.d.cts +3 -4
- package/dist/index.d.ts +3 -4
- package/dist/index.js +130 -126
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -31,138 +31,142 @@ let path = require("path");
|
|
|
31
31
|
path = __toESM(path);
|
|
32
32
|
|
|
33
33
|
//#region src/supabaseDatabase.ts
|
|
34
|
-
const supabaseDatabase = (
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
if (!data || error) return null;
|
|
34
|
+
const supabaseDatabase = (0, __hot_updater_plugin_core.createDatabasePlugin)({
|
|
35
|
+
name: "supabaseDatabase",
|
|
36
|
+
factory: (config) => {
|
|
37
|
+
const supabase = (0, __supabase_supabase_js.createClient)(config.supabaseUrl, config.supabaseAnonKey);
|
|
39
38
|
return {
|
|
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
|
-
|
|
39
|
+
async getBundleById(bundleId) {
|
|
40
|
+
const { data, error } = await supabase.from("bundles").select("channel, enabled, should_force_update, file_hash, git_commit_hash, id, message, platform, target_app_version, fingerprint_hash, storage_uri, metadata").eq("id", bundleId).single();
|
|
41
|
+
if (!data || error) return null;
|
|
42
|
+
return {
|
|
43
|
+
channel: data.channel,
|
|
44
|
+
enabled: data.enabled,
|
|
45
|
+
shouldForceUpdate: data.should_force_update,
|
|
46
|
+
fileHash: data.file_hash,
|
|
47
|
+
gitCommitHash: data.git_commit_hash,
|
|
48
|
+
id: data.id,
|
|
49
|
+
message: data.message,
|
|
50
|
+
platform: data.platform,
|
|
51
|
+
targetAppVersion: data.target_app_version,
|
|
52
|
+
fingerprintHash: data.fingerprint_hash,
|
|
53
|
+
storageUri: data.storage_uri,
|
|
54
|
+
metadata: data.metadata ?? {}
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
async getBundles(options) {
|
|
58
|
+
const { where, limit, offset } = options ?? {};
|
|
59
|
+
let countQuery = supabase.from("bundles").select("*", {
|
|
60
|
+
count: "exact",
|
|
61
|
+
head: true
|
|
62
|
+
});
|
|
63
|
+
if (where?.channel) countQuery = countQuery.eq("channel", where.channel);
|
|
64
|
+
if (where?.platform) countQuery = countQuery.eq("platform", where.platform);
|
|
65
|
+
const { count: total = 0 } = await countQuery;
|
|
66
|
+
let query = supabase.from("bundles").select("id, channel, enabled, platform, should_force_update, file_hash, git_commit_hash, message, fingerprint_hash, target_app_version, storage_uri, metadata").order("id", { ascending: false });
|
|
67
|
+
if (where?.channel) query = query.eq("channel", where.channel);
|
|
68
|
+
if (where?.platform) query = query.eq("platform", where.platform);
|
|
69
|
+
if (limit) query = query.limit(limit);
|
|
70
|
+
if (offset) query = query.range(offset, offset + (limit || 20) - 1);
|
|
71
|
+
const { data } = await query;
|
|
72
|
+
return {
|
|
73
|
+
data: data ? data.map((bundle) => ({
|
|
74
|
+
channel: bundle.channel,
|
|
75
|
+
enabled: bundle.enabled,
|
|
76
|
+
shouldForceUpdate: bundle.should_force_update,
|
|
77
|
+
fileHash: bundle.file_hash,
|
|
78
|
+
gitCommitHash: bundle.git_commit_hash,
|
|
79
|
+
id: bundle.id,
|
|
80
|
+
message: bundle.message,
|
|
81
|
+
platform: bundle.platform,
|
|
82
|
+
targetAppVersion: bundle.target_app_version,
|
|
83
|
+
fingerprintHash: bundle.fingerprint_hash,
|
|
84
|
+
storageUri: bundle.storage_uri,
|
|
85
|
+
metadata: bundle.metadata ?? {}
|
|
86
|
+
})) : [],
|
|
87
|
+
pagination: (0, __hot_updater_plugin_core.calculatePagination)(total ?? 0, {
|
|
88
|
+
limit,
|
|
89
|
+
offset
|
|
90
|
+
})
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
async getChannels() {
|
|
94
|
+
const { data, error } = await supabase.rpc("get_channels");
|
|
95
|
+
if (error) throw error;
|
|
96
|
+
return data.map((bundle) => bundle.channel);
|
|
97
|
+
},
|
|
98
|
+
async commitBundle({ changedSets }) {
|
|
99
|
+
if (changedSets.length === 0) return;
|
|
100
|
+
for (const op of changedSets) if (op.operation === "delete") {
|
|
101
|
+
const { error } = await supabase.from("bundles").delete().eq("id", op.data.id);
|
|
102
|
+
if (error) throw new Error(`Failed to delete bundle: ${error.message}`);
|
|
103
|
+
} else if (op.operation === "insert" || op.operation === "update") {
|
|
104
|
+
const bundle = op.data;
|
|
105
|
+
const { error } = await supabase.from("bundles").upsert({
|
|
106
|
+
id: bundle.id,
|
|
107
|
+
channel: bundle.channel,
|
|
108
|
+
enabled: bundle.enabled,
|
|
109
|
+
should_force_update: bundle.shouldForceUpdate,
|
|
110
|
+
file_hash: bundle.fileHash,
|
|
111
|
+
git_commit_hash: bundle.gitCommitHash,
|
|
112
|
+
message: bundle.message,
|
|
113
|
+
platform: bundle.platform,
|
|
114
|
+
target_app_version: bundle.targetAppVersion,
|
|
115
|
+
fingerprint_hash: bundle.fingerprintHash,
|
|
116
|
+
storage_uri: bundle.storageUri,
|
|
117
|
+
metadata: bundle.metadata
|
|
118
|
+
}, { onConflict: "id" });
|
|
119
|
+
if (error) throw error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
88
122
|
};
|
|
89
|
-
},
|
|
90
|
-
async getChannels(context) {
|
|
91
|
-
const { data, error } = await context.supabase.rpc("get_channels");
|
|
92
|
-
if (error) throw error;
|
|
93
|
-
return data.map((bundle) => bundle.channel);
|
|
94
|
-
},
|
|
95
|
-
async commitBundle(context, { changedSets }) {
|
|
96
|
-
if (changedSets.length === 0) return;
|
|
97
|
-
for (const op of changedSets) if (op.operation === "delete") {
|
|
98
|
-
const { error } = await context.supabase.from("bundles").delete().eq("id", op.data.id);
|
|
99
|
-
if (error) throw new Error(`Failed to delete bundle: ${error.message}`);
|
|
100
|
-
} else if (op.operation === "insert" || op.operation === "update") {
|
|
101
|
-
const bundle = op.data;
|
|
102
|
-
const { error } = await context.supabase.from("bundles").upsert({
|
|
103
|
-
id: bundle.id,
|
|
104
|
-
channel: bundle.channel,
|
|
105
|
-
enabled: bundle.enabled,
|
|
106
|
-
should_force_update: bundle.shouldForceUpdate,
|
|
107
|
-
file_hash: bundle.fileHash,
|
|
108
|
-
git_commit_hash: bundle.gitCommitHash,
|
|
109
|
-
message: bundle.message,
|
|
110
|
-
platform: bundle.platform,
|
|
111
|
-
target_app_version: bundle.targetAppVersion,
|
|
112
|
-
fingerprint_hash: bundle.fingerprintHash,
|
|
113
|
-
storage_uri: bundle.storageUri,
|
|
114
|
-
metadata: bundle.metadata
|
|
115
|
-
}, { onConflict: "id" });
|
|
116
|
-
if (error) throw error;
|
|
117
|
-
}
|
|
118
|
-
hooks?.onDatabaseUpdated?.();
|
|
119
123
|
}
|
|
120
|
-
}
|
|
124
|
+
});
|
|
121
125
|
|
|
122
126
|
//#endregion
|
|
123
127
|
//#region src/supabaseStorage.ts
|
|
124
|
-
const supabaseStorage = (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (error
|
|
136
|
-
|
|
128
|
+
const supabaseStorage = (0, __hot_updater_plugin_core.createStoragePlugin)({
|
|
129
|
+
name: "supabaseStorage",
|
|
130
|
+
supportedProtocol: "supabase-storage",
|
|
131
|
+
factory: (config) => {
|
|
132
|
+
const bucket = (0, __supabase_supabase_js.createClient)(config.supabaseUrl, config.supabaseAnonKey).storage.from(config.bucketName);
|
|
133
|
+
const getStorageKey = (0, __hot_updater_plugin_core.createStorageKeyBuilder)(config.basePath);
|
|
134
|
+
return {
|
|
135
|
+
async delete(storageUri) {
|
|
136
|
+
const { key, bucket: bucketName } = (0, __hot_updater_plugin_core.parseStorageUri)(storageUri, "supabase-storage");
|
|
137
|
+
if (bucketName !== config.bucketName) throw new Error(`Bucket name mismatch: expected "${config.bucketName}", but found "${bucketName}".`);
|
|
138
|
+
const { error } = await bucket.remove([key]);
|
|
139
|
+
if (error) {
|
|
140
|
+
if (error.message?.includes("not found")) throw new Error(`Bundle not found`);
|
|
141
|
+
throw new Error(`Failed to delete bundle: ${error.message}`);
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
async upload(key, filePath) {
|
|
145
|
+
const Body = await fs_promises.default.readFile(filePath);
|
|
146
|
+
const ContentType = (0, __hot_updater_plugin_core.getContentType)(filePath);
|
|
147
|
+
const Key = getStorageKey(key, path.default.basename(filePath));
|
|
148
|
+
const upload = await bucket.upload(Key, Body, {
|
|
149
|
+
contentType: ContentType,
|
|
150
|
+
cacheControl: "max-age=31536000",
|
|
151
|
+
headers: {}
|
|
152
|
+
});
|
|
153
|
+
if (upload.error) throw upload.error;
|
|
154
|
+
return { storageUri: `supabase-storage://${upload.data.fullPath}` };
|
|
155
|
+
},
|
|
156
|
+
async getDownloadUrl(storageUri) {
|
|
157
|
+
const u = new URL(storageUri);
|
|
158
|
+
if (u.protocol.replace(":", "") !== "supabase-storage") throw new Error("Invalid Supabase storage URI protocol");
|
|
159
|
+
let key = `${u.host}${u.pathname}`.replace(/^\//, "");
|
|
160
|
+
if (!key) throw new Error("Invalid Supabase storage URI: missing key");
|
|
161
|
+
if (key.startsWith(`${config.bucketName}/`)) key = key.substring(`${config.bucketName}/`.length);
|
|
162
|
+
const { data, error } = await bucket.createSignedUrl(key, 3600);
|
|
163
|
+
if (error) throw new Error(`Failed to generate download URL: ${error.message}`);
|
|
164
|
+
if (!data?.signedUrl) throw new Error("Failed to generate download URL");
|
|
165
|
+
return { fileUrl: data.signedUrl };
|
|
137
166
|
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const ContentType = (0, __hot_updater_plugin_core.getContentType)(filePath);
|
|
142
|
-
const Key = getStorageKey(key, path.default.basename(filePath));
|
|
143
|
-
const upload = await bucket.upload(Key, Body, {
|
|
144
|
-
contentType: ContentType,
|
|
145
|
-
cacheControl: "max-age=31536000",
|
|
146
|
-
headers: {}
|
|
147
|
-
});
|
|
148
|
-
if (upload.error) throw upload.error;
|
|
149
|
-
const fullPath = upload.data.fullPath;
|
|
150
|
-
hooks?.onStorageUploaded?.();
|
|
151
|
-
return { storageUri: `supabase-storage://${fullPath}` };
|
|
152
|
-
},
|
|
153
|
-
async getDownloadUrl(storageUri) {
|
|
154
|
-
const u = new URL(storageUri);
|
|
155
|
-
if (u.protocol.replace(":", "") !== "supabase-storage") throw new Error("Invalid Supabase storage URI protocol");
|
|
156
|
-
let key = `${u.host}${u.pathname}`.replace(/^\//, "");
|
|
157
|
-
if (!key) throw new Error("Invalid Supabase storage URI: missing key");
|
|
158
|
-
if (key.startsWith(`${config.bucketName}/`)) key = key.substring(`${config.bucketName}/`.length);
|
|
159
|
-
const { data, error } = await bucket.createSignedUrl(key, 3600);
|
|
160
|
-
if (error) throw new Error(`Failed to generate download URL: ${error.message}`);
|
|
161
|
-
if (!data?.signedUrl) throw new Error("Failed to generate download URL");
|
|
162
|
-
return { fileUrl: data.signedUrl };
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
};
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
});
|
|
166
170
|
|
|
167
171
|
//#endregion
|
|
168
172
|
exports.supabaseDatabase = supabaseDatabase;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { BasePluginArgs, DatabasePluginHooks, StoragePlugin, StoragePluginHooks } from "@hot-updater/plugin-core";
|
|
1
|
+
import * as _hot_updater_plugin_core1 from "@hot-updater/plugin-core";
|
|
3
2
|
|
|
4
3
|
//#region src/supabaseDatabase.d.ts
|
|
5
4
|
interface SupabaseDatabaseConfig {
|
|
6
5
|
supabaseUrl: string;
|
|
7
6
|
supabaseAnonKey: string;
|
|
8
7
|
}
|
|
9
|
-
declare const supabaseDatabase: (config: SupabaseDatabaseConfig, hooks?: DatabasePluginHooks) => (
|
|
8
|
+
declare const supabaseDatabase: (config: SupabaseDatabaseConfig, hooks?: _hot_updater_plugin_core1.DatabasePluginHooks) => (() => _hot_updater_plugin_core1.DatabasePlugin);
|
|
10
9
|
//#endregion
|
|
11
10
|
//#region src/supabaseStorage.d.ts
|
|
12
11
|
interface SupabaseStorageConfig {
|
|
@@ -18,6 +17,6 @@ interface SupabaseStorageConfig {
|
|
|
18
17
|
*/
|
|
19
18
|
basePath?: string;
|
|
20
19
|
}
|
|
21
|
-
declare const supabaseStorage: (config: SupabaseStorageConfig, hooks?: StoragePluginHooks) => (
|
|
20
|
+
declare const supabaseStorage: (config: SupabaseStorageConfig, hooks?: _hot_updater_plugin_core1.StoragePluginHooks) => () => _hot_updater_plugin_core1.StoragePlugin;
|
|
22
21
|
//#endregion
|
|
23
22
|
export { SupabaseDatabaseConfig, SupabaseStorageConfig, supabaseDatabase, supabaseStorage };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { BasePluginArgs, DatabasePluginHooks, StoragePlugin, StoragePluginHooks } from "@hot-updater/plugin-core";
|
|
1
|
+
import * as _hot_updater_plugin_core1 from "@hot-updater/plugin-core";
|
|
3
2
|
|
|
4
3
|
//#region src/supabaseDatabase.d.ts
|
|
5
4
|
interface SupabaseDatabaseConfig {
|
|
6
5
|
supabaseUrl: string;
|
|
7
6
|
supabaseAnonKey: string;
|
|
8
7
|
}
|
|
9
|
-
declare const supabaseDatabase: (config: SupabaseDatabaseConfig, hooks?: DatabasePluginHooks) => (
|
|
8
|
+
declare const supabaseDatabase: (config: SupabaseDatabaseConfig, hooks?: _hot_updater_plugin_core1.DatabasePluginHooks) => (() => _hot_updater_plugin_core1.DatabasePlugin);
|
|
10
9
|
//#endregion
|
|
11
10
|
//#region src/supabaseStorage.d.ts
|
|
12
11
|
interface SupabaseStorageConfig {
|
|
@@ -18,6 +17,6 @@ interface SupabaseStorageConfig {
|
|
|
18
17
|
*/
|
|
19
18
|
basePath?: string;
|
|
20
19
|
}
|
|
21
|
-
declare const supabaseStorage: (config: SupabaseStorageConfig, hooks?: StoragePluginHooks) => (
|
|
20
|
+
declare const supabaseStorage: (config: SupabaseStorageConfig, hooks?: _hot_updater_plugin_core1.StoragePluginHooks) => () => _hot_updater_plugin_core1.StoragePlugin;
|
|
22
21
|
//#endregion
|
|
23
22
|
export { SupabaseDatabaseConfig, SupabaseStorageConfig, supabaseDatabase, supabaseStorage };
|
package/dist/index.js
CHANGED
|
@@ -1,141 +1,145 @@
|
|
|
1
|
-
import { calculatePagination, createDatabasePlugin, createStorageKeyBuilder, getContentType, parseStorageUri } from "@hot-updater/plugin-core";
|
|
1
|
+
import { calculatePagination, createDatabasePlugin, createStorageKeyBuilder, createStoragePlugin, getContentType, parseStorageUri } from "@hot-updater/plugin-core";
|
|
2
2
|
import { createClient } from "@supabase/supabase-js";
|
|
3
3
|
import fs from "fs/promises";
|
|
4
4
|
import path from "path";
|
|
5
5
|
|
|
6
6
|
//#region src/supabaseDatabase.ts
|
|
7
|
-
const supabaseDatabase =
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
if (!data || error) return null;
|
|
7
|
+
const supabaseDatabase = createDatabasePlugin({
|
|
8
|
+
name: "supabaseDatabase",
|
|
9
|
+
factory: (config) => {
|
|
10
|
+
const supabase = createClient(config.supabaseUrl, config.supabaseAnonKey);
|
|
12
11
|
return {
|
|
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
|
-
|
|
12
|
+
async getBundleById(bundleId) {
|
|
13
|
+
const { data, error } = await supabase.from("bundles").select("channel, enabled, should_force_update, file_hash, git_commit_hash, id, message, platform, target_app_version, fingerprint_hash, storage_uri, metadata").eq("id", bundleId).single();
|
|
14
|
+
if (!data || error) return null;
|
|
15
|
+
return {
|
|
16
|
+
channel: data.channel,
|
|
17
|
+
enabled: data.enabled,
|
|
18
|
+
shouldForceUpdate: data.should_force_update,
|
|
19
|
+
fileHash: data.file_hash,
|
|
20
|
+
gitCommitHash: data.git_commit_hash,
|
|
21
|
+
id: data.id,
|
|
22
|
+
message: data.message,
|
|
23
|
+
platform: data.platform,
|
|
24
|
+
targetAppVersion: data.target_app_version,
|
|
25
|
+
fingerprintHash: data.fingerprint_hash,
|
|
26
|
+
storageUri: data.storage_uri,
|
|
27
|
+
metadata: data.metadata ?? {}
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
async getBundles(options) {
|
|
31
|
+
const { where, limit, offset } = options ?? {};
|
|
32
|
+
let countQuery = supabase.from("bundles").select("*", {
|
|
33
|
+
count: "exact",
|
|
34
|
+
head: true
|
|
35
|
+
});
|
|
36
|
+
if (where?.channel) countQuery = countQuery.eq("channel", where.channel);
|
|
37
|
+
if (where?.platform) countQuery = countQuery.eq("platform", where.platform);
|
|
38
|
+
const { count: total = 0 } = await countQuery;
|
|
39
|
+
let query = supabase.from("bundles").select("id, channel, enabled, platform, should_force_update, file_hash, git_commit_hash, message, fingerprint_hash, target_app_version, storage_uri, metadata").order("id", { ascending: false });
|
|
40
|
+
if (where?.channel) query = query.eq("channel", where.channel);
|
|
41
|
+
if (where?.platform) query = query.eq("platform", where.platform);
|
|
42
|
+
if (limit) query = query.limit(limit);
|
|
43
|
+
if (offset) query = query.range(offset, offset + (limit || 20) - 1);
|
|
44
|
+
const { data } = await query;
|
|
45
|
+
return {
|
|
46
|
+
data: data ? data.map((bundle) => ({
|
|
47
|
+
channel: bundle.channel,
|
|
48
|
+
enabled: bundle.enabled,
|
|
49
|
+
shouldForceUpdate: bundle.should_force_update,
|
|
50
|
+
fileHash: bundle.file_hash,
|
|
51
|
+
gitCommitHash: bundle.git_commit_hash,
|
|
52
|
+
id: bundle.id,
|
|
53
|
+
message: bundle.message,
|
|
54
|
+
platform: bundle.platform,
|
|
55
|
+
targetAppVersion: bundle.target_app_version,
|
|
56
|
+
fingerprintHash: bundle.fingerprint_hash,
|
|
57
|
+
storageUri: bundle.storage_uri,
|
|
58
|
+
metadata: bundle.metadata ?? {}
|
|
59
|
+
})) : [],
|
|
60
|
+
pagination: calculatePagination(total ?? 0, {
|
|
61
|
+
limit,
|
|
62
|
+
offset
|
|
63
|
+
})
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
async getChannels() {
|
|
67
|
+
const { data, error } = await supabase.rpc("get_channels");
|
|
68
|
+
if (error) throw error;
|
|
69
|
+
return data.map((bundle) => bundle.channel);
|
|
70
|
+
},
|
|
71
|
+
async commitBundle({ changedSets }) {
|
|
72
|
+
if (changedSets.length === 0) return;
|
|
73
|
+
for (const op of changedSets) if (op.operation === "delete") {
|
|
74
|
+
const { error } = await supabase.from("bundles").delete().eq("id", op.data.id);
|
|
75
|
+
if (error) throw new Error(`Failed to delete bundle: ${error.message}`);
|
|
76
|
+
} else if (op.operation === "insert" || op.operation === "update") {
|
|
77
|
+
const bundle = op.data;
|
|
78
|
+
const { error } = await supabase.from("bundles").upsert({
|
|
79
|
+
id: bundle.id,
|
|
80
|
+
channel: bundle.channel,
|
|
81
|
+
enabled: bundle.enabled,
|
|
82
|
+
should_force_update: bundle.shouldForceUpdate,
|
|
83
|
+
file_hash: bundle.fileHash,
|
|
84
|
+
git_commit_hash: bundle.gitCommitHash,
|
|
85
|
+
message: bundle.message,
|
|
86
|
+
platform: bundle.platform,
|
|
87
|
+
target_app_version: bundle.targetAppVersion,
|
|
88
|
+
fingerprint_hash: bundle.fingerprintHash,
|
|
89
|
+
storage_uri: bundle.storageUri,
|
|
90
|
+
metadata: bundle.metadata
|
|
91
|
+
}, { onConflict: "id" });
|
|
92
|
+
if (error) throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
61
95
|
};
|
|
62
|
-
},
|
|
63
|
-
async getChannels(context) {
|
|
64
|
-
const { data, error } = await context.supabase.rpc("get_channels");
|
|
65
|
-
if (error) throw error;
|
|
66
|
-
return data.map((bundle) => bundle.channel);
|
|
67
|
-
},
|
|
68
|
-
async commitBundle(context, { changedSets }) {
|
|
69
|
-
if (changedSets.length === 0) return;
|
|
70
|
-
for (const op of changedSets) if (op.operation === "delete") {
|
|
71
|
-
const { error } = await context.supabase.from("bundles").delete().eq("id", op.data.id);
|
|
72
|
-
if (error) throw new Error(`Failed to delete bundle: ${error.message}`);
|
|
73
|
-
} else if (op.operation === "insert" || op.operation === "update") {
|
|
74
|
-
const bundle = op.data;
|
|
75
|
-
const { error } = await context.supabase.from("bundles").upsert({
|
|
76
|
-
id: bundle.id,
|
|
77
|
-
channel: bundle.channel,
|
|
78
|
-
enabled: bundle.enabled,
|
|
79
|
-
should_force_update: bundle.shouldForceUpdate,
|
|
80
|
-
file_hash: bundle.fileHash,
|
|
81
|
-
git_commit_hash: bundle.gitCommitHash,
|
|
82
|
-
message: bundle.message,
|
|
83
|
-
platform: bundle.platform,
|
|
84
|
-
target_app_version: bundle.targetAppVersion,
|
|
85
|
-
fingerprint_hash: bundle.fingerprintHash,
|
|
86
|
-
storage_uri: bundle.storageUri,
|
|
87
|
-
metadata: bundle.metadata
|
|
88
|
-
}, { onConflict: "id" });
|
|
89
|
-
if (error) throw error;
|
|
90
|
-
}
|
|
91
|
-
hooks?.onDatabaseUpdated?.();
|
|
92
96
|
}
|
|
93
|
-
}
|
|
97
|
+
});
|
|
94
98
|
|
|
95
99
|
//#endregion
|
|
96
100
|
//#region src/supabaseStorage.ts
|
|
97
|
-
const supabaseStorage = (
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (error
|
|
109
|
-
|
|
101
|
+
const supabaseStorage = createStoragePlugin({
|
|
102
|
+
name: "supabaseStorage",
|
|
103
|
+
supportedProtocol: "supabase-storage",
|
|
104
|
+
factory: (config) => {
|
|
105
|
+
const bucket = createClient(config.supabaseUrl, config.supabaseAnonKey).storage.from(config.bucketName);
|
|
106
|
+
const getStorageKey = createStorageKeyBuilder(config.basePath);
|
|
107
|
+
return {
|
|
108
|
+
async delete(storageUri) {
|
|
109
|
+
const { key, bucket: bucketName } = parseStorageUri(storageUri, "supabase-storage");
|
|
110
|
+
if (bucketName !== config.bucketName) throw new Error(`Bucket name mismatch: expected "${config.bucketName}", but found "${bucketName}".`);
|
|
111
|
+
const { error } = await bucket.remove([key]);
|
|
112
|
+
if (error) {
|
|
113
|
+
if (error.message?.includes("not found")) throw new Error(`Bundle not found`);
|
|
114
|
+
throw new Error(`Failed to delete bundle: ${error.message}`);
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
async upload(key, filePath) {
|
|
118
|
+
const Body = await fs.readFile(filePath);
|
|
119
|
+
const ContentType = getContentType(filePath);
|
|
120
|
+
const Key = getStorageKey(key, path.basename(filePath));
|
|
121
|
+
const upload = await bucket.upload(Key, Body, {
|
|
122
|
+
contentType: ContentType,
|
|
123
|
+
cacheControl: "max-age=31536000",
|
|
124
|
+
headers: {}
|
|
125
|
+
});
|
|
126
|
+
if (upload.error) throw upload.error;
|
|
127
|
+
return { storageUri: `supabase-storage://${upload.data.fullPath}` };
|
|
128
|
+
},
|
|
129
|
+
async getDownloadUrl(storageUri) {
|
|
130
|
+
const u = new URL(storageUri);
|
|
131
|
+
if (u.protocol.replace(":", "") !== "supabase-storage") throw new Error("Invalid Supabase storage URI protocol");
|
|
132
|
+
let key = `${u.host}${u.pathname}`.replace(/^\//, "");
|
|
133
|
+
if (!key) throw new Error("Invalid Supabase storage URI: missing key");
|
|
134
|
+
if (key.startsWith(`${config.bucketName}/`)) key = key.substring(`${config.bucketName}/`.length);
|
|
135
|
+
const { data, error } = await bucket.createSignedUrl(key, 3600);
|
|
136
|
+
if (error) throw new Error(`Failed to generate download URL: ${error.message}`);
|
|
137
|
+
if (!data?.signedUrl) throw new Error("Failed to generate download URL");
|
|
138
|
+
return { fileUrl: data.signedUrl };
|
|
110
139
|
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const ContentType = getContentType(filePath);
|
|
115
|
-
const Key = getStorageKey(key, path.basename(filePath));
|
|
116
|
-
const upload = await bucket.upload(Key, Body, {
|
|
117
|
-
contentType: ContentType,
|
|
118
|
-
cacheControl: "max-age=31536000",
|
|
119
|
-
headers: {}
|
|
120
|
-
});
|
|
121
|
-
if (upload.error) throw upload.error;
|
|
122
|
-
const fullPath = upload.data.fullPath;
|
|
123
|
-
hooks?.onStorageUploaded?.();
|
|
124
|
-
return { storageUri: `supabase-storage://${fullPath}` };
|
|
125
|
-
},
|
|
126
|
-
async getDownloadUrl(storageUri) {
|
|
127
|
-
const u = new URL(storageUri);
|
|
128
|
-
if (u.protocol.replace(":", "") !== "supabase-storage") throw new Error("Invalid Supabase storage URI protocol");
|
|
129
|
-
let key = `${u.host}${u.pathname}`.replace(/^\//, "");
|
|
130
|
-
if (!key) throw new Error("Invalid Supabase storage URI: missing key");
|
|
131
|
-
if (key.startsWith(`${config.bucketName}/`)) key = key.substring(`${config.bucketName}/`.length);
|
|
132
|
-
const { data, error } = await bucket.createSignedUrl(key, 3600);
|
|
133
|
-
if (error) throw new Error(`Failed to generate download URL: ${error.message}`);
|
|
134
|
-
if (!data?.signedUrl) throw new Error("Failed to generate download URL");
|
|
135
|
-
return { fileUrl: data.signedUrl };
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
};
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
});
|
|
139
143
|
|
|
140
144
|
//#endregion
|
|
141
145
|
export { supabaseDatabase, supabaseStorage };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/supabase",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.21.
|
|
4
|
+
"version": "0.21.12",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"module": "dist/index.js",
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@supabase/supabase-js": "^2.76.1",
|
|
44
|
-
"@hot-updater/core": "0.21.
|
|
45
|
-
"@hot-updater/plugin-core": "0.21.
|
|
46
|
-
"@hot-updater/cli-tools": "0.21.
|
|
44
|
+
"@hot-updater/core": "0.21.12",
|
|
45
|
+
"@hot-updater/plugin-core": "0.21.12",
|
|
46
|
+
"@hot-updater/cli-tools": "0.21.12"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"dayjs": "^1.11.13",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"execa": "9.5.2",
|
|
52
52
|
"@types/node": "^20",
|
|
53
53
|
"mime": "^4.0.4",
|
|
54
|
-
"@hot-updater/postgres": "0.21.
|
|
54
|
+
"@hot-updater/postgres": "0.21.12"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build": "tsdown",
|