@rimori/client 1.1.1 → 1.1.2
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/plugin/PluginController.d.ts +1 -1
- package/dist/plugin/PluginController.js +16 -7
- package/dist/plugin/StandaloneClient.d.ts +16 -0
- package/dist/plugin/StandaloneClient.js +76 -0
- package/dist/plugin/fromRimori/SupabaseHandler.d.ts +13 -0
- package/dist/plugin/fromRimori/SupabaseHandler.js +55 -0
- package/dist/providers/PluginProvider.js +42 -3
- package/package.json +1 -1
- package/src/plugin/PluginController.ts +18 -8
- package/src/plugin/StandaloneClient.ts +73 -0
- package/src/providers/PluginProvider.tsx +48 -3
|
@@ -8,7 +8,7 @@ export declare class PluginController {
|
|
|
8
8
|
private supabaseInfo;
|
|
9
9
|
private pluginId;
|
|
10
10
|
private constructor();
|
|
11
|
-
static getInstance(
|
|
11
|
+
static getInstance(pluginId: string, standalone?: boolean): Promise<RimoriClient>;
|
|
12
12
|
private getSecret;
|
|
13
13
|
getClient(): Promise<{
|
|
14
14
|
supabase: SupabaseClient;
|
|
@@ -8,11 +8,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { createClient } from '@supabase/supabase-js';
|
|
11
|
-
import { RimoriClient } from "./RimoriClient";
|
|
12
11
|
import { EventBus } from './fromRimori/EventBus';
|
|
12
|
+
import { RimoriClient } from "./RimoriClient";
|
|
13
13
|
import { setTheme } from './ThemeSetter';
|
|
14
|
+
import { StandaloneClient } from './StandaloneClient';
|
|
14
15
|
export class PluginController {
|
|
15
|
-
constructor(pluginId) {
|
|
16
|
+
constructor(pluginId, standalone) {
|
|
16
17
|
this.communicationSecret = null;
|
|
17
18
|
this.supabase = null;
|
|
18
19
|
this.supabaseInfo = null;
|
|
@@ -21,6 +22,9 @@ export class PluginController {
|
|
|
21
22
|
if (typeof WorkerGlobalScope === 'undefined') {
|
|
22
23
|
setTheme();
|
|
23
24
|
}
|
|
25
|
+
//no need to forward messages to parent in standalone mode
|
|
26
|
+
if (standalone)
|
|
27
|
+
return;
|
|
24
28
|
window.addEventListener("message", (event) => {
|
|
25
29
|
// console.log("client: message received", event);
|
|
26
30
|
const { topic, sender, data, eventId } = event.data.event;
|
|
@@ -29,19 +33,24 @@ export class PluginController {
|
|
|
29
33
|
return;
|
|
30
34
|
EventBus.emit(sender, topic, data, eventId);
|
|
31
35
|
});
|
|
36
|
+
const secret = this.getSecret();
|
|
32
37
|
EventBus.on("*", (event) => {
|
|
33
38
|
// skip messages which are not from the own plugin
|
|
34
39
|
if (event.sender !== this.pluginId)
|
|
35
40
|
return;
|
|
36
41
|
if (event.topic.startsWith("self."))
|
|
37
42
|
return;
|
|
38
|
-
|
|
43
|
+
// console.log("sending event to parent", event);
|
|
44
|
+
window.parent.postMessage({ event, secret }, "*");
|
|
39
45
|
});
|
|
40
46
|
}
|
|
41
|
-
static getInstance(
|
|
42
|
-
return __awaiter(this,
|
|
47
|
+
static getInstance(pluginId_1) {
|
|
48
|
+
return __awaiter(this, arguments, void 0, function* (pluginId, standalone = false) {
|
|
43
49
|
if (!PluginController.instance) {
|
|
44
|
-
|
|
50
|
+
if (standalone) {
|
|
51
|
+
yield StandaloneClient.initListeners(pluginId);
|
|
52
|
+
}
|
|
53
|
+
PluginController.instance = new PluginController(pluginId, standalone);
|
|
45
54
|
PluginController.client = yield RimoriClient.getInstance(PluginController.instance);
|
|
46
55
|
}
|
|
47
56
|
return PluginController.client;
|
|
@@ -51,7 +60,7 @@ export class PluginController {
|
|
|
51
60
|
if (!this.communicationSecret) {
|
|
52
61
|
const secret = new URLSearchParams(window.location.search).get("secret");
|
|
53
62
|
if (!secret) {
|
|
54
|
-
|
|
63
|
+
console.info("Communication secret not found in URL as query parameter");
|
|
55
64
|
}
|
|
56
65
|
this.communicationSecret = secret;
|
|
57
66
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
export interface StandaloneConfig {
|
|
3
|
+
url: string;
|
|
4
|
+
key: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class StandaloneClient {
|
|
7
|
+
private static instance;
|
|
8
|
+
private config;
|
|
9
|
+
private supabase;
|
|
10
|
+
private constructor();
|
|
11
|
+
static getInstance(): Promise<StandaloneClient>;
|
|
12
|
+
getClient(): Promise<SupabaseClient>;
|
|
13
|
+
needsLogin(): Promise<boolean>;
|
|
14
|
+
login(email: string, password: string): Promise<boolean>;
|
|
15
|
+
static initListeners(pluginId: string): Promise<void>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { EventBus } from "./fromRimori/EventBus";
|
|
11
|
+
import { createClient } from "@supabase/supabase-js";
|
|
12
|
+
export class StandaloneClient {
|
|
13
|
+
constructor(config) {
|
|
14
|
+
this.supabase = createClient(config.url, config.key);
|
|
15
|
+
this.config = config;
|
|
16
|
+
}
|
|
17
|
+
static getInstance() {
|
|
18
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19
|
+
if (!StandaloneClient.instance) {
|
|
20
|
+
const config = yield fetch("http://localhost:3000/config.json").then(res => res.json());
|
|
21
|
+
StandaloneClient.instance = new StandaloneClient({ url: config.SUPABASE_URL, key: config.SUPABASE_ANON_KEY });
|
|
22
|
+
}
|
|
23
|
+
return StandaloneClient.instance;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
getClient() {
|
|
27
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
28
|
+
return this.supabase;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
needsLogin() {
|
|
32
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
33
|
+
const { error } = yield this.supabase.auth.getUser();
|
|
34
|
+
return error !== null;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
login(email, password) {
|
|
38
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
39
|
+
const { error } = yield this.supabase.auth.signInWithPassword({ email, password });
|
|
40
|
+
if (error) {
|
|
41
|
+
console.error("Login failed:", error);
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
console.log("Successfully logged in");
|
|
45
|
+
return true;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
static initListeners(pluginId) {
|
|
49
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
50
|
+
console.warn("The plugin seams to not be running inside the Rimori platform. Switching to development standalone mode.");
|
|
51
|
+
// console.log("event that needs to be handled", event);
|
|
52
|
+
const { supabase, config } = yield StandaloneClient.getInstance();
|
|
53
|
+
// EventBus.on("*", async (event) => {
|
|
54
|
+
EventBus.respond("standalone", "global.supabase.requestAccess", () => __awaiter(this, void 0, void 0, function* () {
|
|
55
|
+
var _a;
|
|
56
|
+
const session = yield supabase.auth.getSession();
|
|
57
|
+
console.log("session", session);
|
|
58
|
+
const { data, error } = yield supabase.functions.invoke("plugin-token", { headers: { authorization: `Bearer ${(_a = session.data.session) === null || _a === void 0 ? void 0 : _a.access_token}` } });
|
|
59
|
+
if (error) {
|
|
60
|
+
throw new Error("Failed to get plugin token. " + error.message);
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
token: data.token,
|
|
64
|
+
pluginId: pluginId,
|
|
65
|
+
url: config.url,
|
|
66
|
+
key: config.key,
|
|
67
|
+
tablePrefix: pluginId,
|
|
68
|
+
expiration: new Date(Date.now() + 1000 * 60 * 60 * 1.5), // 1.5 hours
|
|
69
|
+
};
|
|
70
|
+
}));
|
|
71
|
+
EventBus.on("*", (event) => __awaiter(this, void 0, void 0, function* () {
|
|
72
|
+
console.log("[standalone] would send event to parent", event);
|
|
73
|
+
}), ["standalone"]);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Plugin } from "@/types/PluginTypes";
|
|
2
|
+
export declare class SupabaseHandler {
|
|
3
|
+
getAccessData(plugin: Plugin): Promise<{
|
|
4
|
+
token: any;
|
|
5
|
+
pluginId: any;
|
|
6
|
+
url: any;
|
|
7
|
+
key: any;
|
|
8
|
+
tablePrefix: any;
|
|
9
|
+
expiration: Date;
|
|
10
|
+
}>;
|
|
11
|
+
getPluginAuthToken(): Promise<any>;
|
|
12
|
+
static init(): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { EventBus } from "../handler/EventBus";
|
|
11
|
+
import { getSupabase, SupabaseClient } from "@/integrations/supabase/client";
|
|
12
|
+
export class SupabaseHandler {
|
|
13
|
+
getAccessData(plugin) {
|
|
14
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
15
|
+
const env = window.APP_CONFIG;
|
|
16
|
+
const token = yield this.getPluginAuthToken();
|
|
17
|
+
return {
|
|
18
|
+
token,
|
|
19
|
+
pluginId: plugin.id,
|
|
20
|
+
url: env.SUPABASE_URL,
|
|
21
|
+
key: env.SUPABASE_ANON_KEY,
|
|
22
|
+
tablePrefix: plugin.id,
|
|
23
|
+
expiration: new Date(Date.now() + 1000 * 60 * 60 * 1.5), // 1.5 hours
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
getPluginAuthToken() {
|
|
28
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
29
|
+
const supabase = getSupabase();
|
|
30
|
+
const { data: { session } } = yield supabase.auth.getSession();
|
|
31
|
+
if (!session) {
|
|
32
|
+
throw new Error("User not logged in");
|
|
33
|
+
}
|
|
34
|
+
// Extract the full-access token for authorization with the Edge Function
|
|
35
|
+
const { data, error } = yield supabase.functions.invoke("plugin-token");
|
|
36
|
+
if (error) {
|
|
37
|
+
console.error("Failed to get plugin token. ", error);
|
|
38
|
+
throw new Error("Failed to get plugin token. " + error.message);
|
|
39
|
+
}
|
|
40
|
+
return data.token;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
static init() {
|
|
44
|
+
const supabaseHandler = new SupabaseHandler();
|
|
45
|
+
EventBus.respond("supabase", "global.supabase.requestAccess", (event) => __awaiter(this, void 0, void 0, function* () {
|
|
46
|
+
const sender = event.sender.split(".")[0];
|
|
47
|
+
const plugins = yield SupabaseClient.getPlugins();
|
|
48
|
+
const plugin = plugins.find((p) => p.id === sender);
|
|
49
|
+
if (!plugin) {
|
|
50
|
+
throw new Error("Plugin not found");
|
|
51
|
+
}
|
|
52
|
+
return yield supabaseHandler.getAccessData(plugin);
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -1,15 +1,34 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
1
10
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
11
|
import { createContext, useContext, useEffect, useState } from 'react';
|
|
3
12
|
import { PluginController } from '../plugin/PluginController';
|
|
4
13
|
import { EventBusHandler } from '../plugin/fromRimori/EventBus';
|
|
5
14
|
import ContextMenu from '../core/components/ContextMenu';
|
|
15
|
+
import { StandaloneClient } from '../plugin/StandaloneClient';
|
|
6
16
|
const PluginContext = createContext(null);
|
|
7
17
|
export const PluginProvider = ({ children, pluginId }) => {
|
|
8
18
|
const [plugin, setPlugin] = useState(null);
|
|
9
|
-
|
|
19
|
+
const [standaloneClient, setStandaloneClient] = useState(false);
|
|
10
20
|
useEffect(() => {
|
|
11
|
-
|
|
12
|
-
|
|
21
|
+
initEventBus(pluginId);
|
|
22
|
+
const standaloneDetected = new URLSearchParams(window.location.search).get("secret") === null;
|
|
23
|
+
if (standaloneDetected && !standaloneClient) {
|
|
24
|
+
StandaloneClient.getInstance().then(client => {
|
|
25
|
+
client.needsLogin().then((needLogin) => setStandaloneClient(needLogin ? client : true));
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
if ((!standaloneDetected && !plugin) || (standaloneDetected && standaloneClient === true)) {
|
|
29
|
+
PluginController.getInstance(pluginId, standaloneDetected).then(setPlugin);
|
|
30
|
+
}
|
|
31
|
+
}, [pluginId, standaloneClient]);
|
|
13
32
|
//route change
|
|
14
33
|
useEffect(() => {
|
|
15
34
|
if (!plugin)
|
|
@@ -38,6 +57,12 @@ export const PluginProvider = ({ children, pluginId }) => {
|
|
|
38
57
|
handleResize();
|
|
39
58
|
return () => body.removeEventListener('resize', handleResize);
|
|
40
59
|
}, [plugin]);
|
|
60
|
+
if (standaloneClient instanceof StandaloneClient) {
|
|
61
|
+
return _jsx(StandaloneAuth, { onLogin: (email, password) => __awaiter(void 0, void 0, void 0, function* () {
|
|
62
|
+
if (yield standaloneClient.login(email, password))
|
|
63
|
+
setStandaloneClient(true);
|
|
64
|
+
}) });
|
|
65
|
+
}
|
|
41
66
|
if (!plugin) {
|
|
42
67
|
return "";
|
|
43
68
|
}
|
|
@@ -55,3 +80,17 @@ function initEventBus(pluginId) {
|
|
|
55
80
|
const isSidebar = url.searchParams.get("applicationMode") === "sidebar";
|
|
56
81
|
EventBusHandler.getInstance("Plugin EventBus " + pluginId + " " + (isSidebar ? "sidebar" : "main"));
|
|
57
82
|
}
|
|
83
|
+
function StandaloneAuth({ onLogin }) {
|
|
84
|
+
const [user, setUser] = useState("");
|
|
85
|
+
const [password, setPassword] = useState("");
|
|
86
|
+
return (_jsx("div", { style: {
|
|
87
|
+
position: 'fixed',
|
|
88
|
+
inset: 0,
|
|
89
|
+
display: 'flex',
|
|
90
|
+
alignItems: 'center',
|
|
91
|
+
justifyContent: 'center',
|
|
92
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
93
|
+
}, children: _jsxs("div", { style: { backgroundColor: '#343534', padding: '1rem', borderRadius: '0.5rem', width: '500px', flexDirection: 'column', display: 'flex', alignItems: 'center', justifyContent: 'center' }, children: [_jsx("p", { style: { fontSize: '2rem', fontWeight: 'bold', marginBottom: '1rem', textAlign: 'center' }, children: "Rimori Login" }), _jsx("p", { style: { marginBottom: '1rem', textAlign: 'center' }, children: "Please login with your Rimori developer account for this plugin to be able to access the Rimori platform the same it will operate in the Rimori platform." }), _jsx("input", { style: { marginBottom: '1rem', width: '100%', padding: '0.5rem', borderRadius: '0.5rem', border: 'none', backgroundColor: '#444444' }, type: "email", placeholder: "Email", onChange: (e) => setUser(e.target.value) }), _jsx("input", { style: { marginBottom: '1rem', width: '100%', padding: '0.5rem', borderRadius: '0.5rem', border: 'none', backgroundColor: '#444444' }, type: "password", placeholder: "Password", onChange: (e) => setPassword(e.target.value) }), _jsx("button", { style: { marginBottom: '1rem', width: '100%', padding: '0.5rem', borderRadius: '0.5rem', border: 'none', backgroundColor: '#928358' }, onClick: () => {
|
|
94
|
+
onLogin(user, password);
|
|
95
|
+
}, children: "Login" })] }) }));
|
|
96
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { createClient, SupabaseClient } from '@supabase/supabase-js'
|
|
2
|
-
import { RimoriClient } from "./RimoriClient";
|
|
1
|
+
import { AuthSessionMissingError, createClient, SupabaseClient } from '@supabase/supabase-js';
|
|
3
2
|
import { EventBus, EventBusMessage } from './fromRimori/EventBus';
|
|
3
|
+
import { RimoriClient } from "./RimoriClient";
|
|
4
4
|
import { setTheme } from './ThemeSetter';
|
|
5
|
+
import { StandaloneClient } from './StandaloneClient';
|
|
5
6
|
|
|
6
7
|
// Add declaration for WorkerGlobalScope
|
|
7
8
|
declare const WorkerGlobalScope: any;
|
|
@@ -23,7 +24,7 @@ export class PluginController {
|
|
|
23
24
|
private supabaseInfo: SupabaseInfo | null = null;
|
|
24
25
|
private pluginId: string;
|
|
25
26
|
|
|
26
|
-
private constructor(pluginId: string) {
|
|
27
|
+
private constructor(pluginId: string, standalone: boolean) {
|
|
27
28
|
this.pluginId = pluginId;
|
|
28
29
|
this.getClient = this.getClient.bind(this);
|
|
29
30
|
|
|
@@ -31,6 +32,9 @@ export class PluginController {
|
|
|
31
32
|
setTheme();
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
//no need to forward messages to parent in standalone mode
|
|
36
|
+
if (standalone) return;
|
|
37
|
+
|
|
34
38
|
window.addEventListener("message", (event) => {
|
|
35
39
|
// console.log("client: message received", event);
|
|
36
40
|
const { topic, sender, data, eventId } = event.data.event as EventBusMessage;
|
|
@@ -41,27 +45,33 @@ export class PluginController {
|
|
|
41
45
|
EventBus.emit(sender, topic, data, eventId);
|
|
42
46
|
});
|
|
43
47
|
|
|
48
|
+
const secret = this.getSecret();
|
|
49
|
+
|
|
44
50
|
EventBus.on("*", (event) => {
|
|
45
51
|
// skip messages which are not from the own plugin
|
|
46
52
|
if (event.sender !== this.pluginId) return;
|
|
47
53
|
if (event.topic.startsWith("self.")) return;
|
|
48
|
-
|
|
54
|
+
// console.log("sending event to parent", event);
|
|
55
|
+
window.parent.postMessage({ event, secret }, "*")
|
|
49
56
|
});
|
|
50
57
|
}
|
|
51
58
|
|
|
52
|
-
public static async getInstance(
|
|
59
|
+
public static async getInstance(pluginId: string, standalone = false): Promise<RimoriClient> {
|
|
53
60
|
if (!PluginController.instance) {
|
|
54
|
-
|
|
61
|
+
if (standalone) {
|
|
62
|
+
await StandaloneClient.initListeners(pluginId);
|
|
63
|
+
}
|
|
64
|
+
PluginController.instance = new PluginController(pluginId, standalone);
|
|
55
65
|
PluginController.client = await RimoriClient.getInstance(PluginController.instance);
|
|
56
66
|
}
|
|
57
67
|
return PluginController.client;
|
|
58
68
|
}
|
|
59
69
|
|
|
60
|
-
private getSecret() {
|
|
70
|
+
private getSecret(): string | null {
|
|
61
71
|
if (!this.communicationSecret) {
|
|
62
72
|
const secret = new URLSearchParams(window.location.search).get("secret");
|
|
63
73
|
if (!secret) {
|
|
64
|
-
|
|
74
|
+
console.info("Communication secret not found in URL as query parameter");
|
|
65
75
|
}
|
|
66
76
|
this.communicationSecret = secret;
|
|
67
77
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { EventBus } from "./fromRimori/EventBus";
|
|
2
|
+
import { createClient, SupabaseClient } from "@supabase/supabase-js";
|
|
3
|
+
|
|
4
|
+
export interface StandaloneConfig {
|
|
5
|
+
url: string,
|
|
6
|
+
key: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class StandaloneClient {
|
|
10
|
+
private static instance: StandaloneClient;
|
|
11
|
+
private config: StandaloneConfig;
|
|
12
|
+
private supabase: SupabaseClient;
|
|
13
|
+
|
|
14
|
+
private constructor(config: StandaloneConfig) {
|
|
15
|
+
this.supabase = createClient(config.url, config.key);
|
|
16
|
+
this.config = config;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public static async getInstance(): Promise<StandaloneClient> {
|
|
20
|
+
if (!StandaloneClient.instance) {
|
|
21
|
+
const config = await fetch("http://localhost:3000/config.json").then(res => res.json());
|
|
22
|
+
StandaloneClient.instance = new StandaloneClient({ url: config.SUPABASE_URL, key: config.SUPABASE_ANON_KEY });
|
|
23
|
+
}
|
|
24
|
+
return StandaloneClient.instance;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public async getClient(): Promise<SupabaseClient> {
|
|
28
|
+
return this.supabase;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public async needsLogin(): Promise<boolean> {
|
|
32
|
+
const { error } = await this.supabase.auth.getUser();
|
|
33
|
+
return error !== null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public async login(email: string, password: string) {
|
|
37
|
+
const { error } = await this.supabase.auth.signInWithPassword({ email, password });
|
|
38
|
+
if (error) {
|
|
39
|
+
console.error("Login failed:", error);
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
console.log("Successfully logged in");
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public static async initListeners(pluginId: string) {
|
|
47
|
+
console.warn("The plugin seams to not be running inside the Rimori platform. Switching to development standalone mode.");
|
|
48
|
+
// console.log("event that needs to be handled", event);
|
|
49
|
+
const { supabase, config } = await StandaloneClient.getInstance();
|
|
50
|
+
|
|
51
|
+
// EventBus.on("*", async (event) => {
|
|
52
|
+
EventBus.respond("standalone", "global.supabase.requestAccess", async () => {
|
|
53
|
+
const session = await supabase.auth.getSession();
|
|
54
|
+
console.log("session", session);
|
|
55
|
+
const { data, error } = await supabase.functions.invoke("plugin-token", { headers: { authorization: `Bearer ${session.data.session?.access_token}` } });
|
|
56
|
+
if (error) {
|
|
57
|
+
throw new Error("Failed to get plugin token. " + error.message);
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
token: data.token,
|
|
61
|
+
pluginId: pluginId,
|
|
62
|
+
url: config.url,
|
|
63
|
+
key: config.key,
|
|
64
|
+
tablePrefix: pluginId,
|
|
65
|
+
expiration: new Date(Date.now() + 1000 * 60 * 60 * 1.5), // 1.5 hours
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
EventBus.on("*", async (event) => {
|
|
70
|
+
console.log("[standalone] would send event to parent", event);
|
|
71
|
+
}, ["standalone"]);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -3,6 +3,7 @@ import { PluginController } from '../plugin/PluginController';
|
|
|
3
3
|
import { RimoriClient } from '../plugin/RimoriClient';
|
|
4
4
|
import { EventBusHandler } from '../plugin/fromRimori/EventBus';
|
|
5
5
|
import ContextMenu from '../core/components/ContextMenu';
|
|
6
|
+
import { StandaloneClient } from '../plugin/StandaloneClient';
|
|
6
7
|
|
|
7
8
|
interface PluginProviderProps {
|
|
8
9
|
children: ReactNode;
|
|
@@ -13,11 +14,21 @@ const PluginContext = createContext<RimoriClient | null>(null);
|
|
|
13
14
|
|
|
14
15
|
export const PluginProvider: React.FC<PluginProviderProps> = ({ children, pluginId }) => {
|
|
15
16
|
const [plugin, setPlugin] = useState<RimoriClient | null>(null);
|
|
16
|
-
|
|
17
|
+
const [standaloneClient, setStandaloneClient] = useState<StandaloneClient | boolean>(false);
|
|
17
18
|
|
|
18
19
|
useEffect(() => {
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
initEventBus(pluginId);
|
|
21
|
+
const standaloneDetected = new URLSearchParams(window.location.search).get("secret") === null;
|
|
22
|
+
if (standaloneDetected && !standaloneClient) {
|
|
23
|
+
StandaloneClient.getInstance().then(client => {
|
|
24
|
+
client.needsLogin().then((needLogin) => setStandaloneClient(needLogin ? client : true));
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if ((!standaloneDetected && !plugin) || (standaloneDetected && standaloneClient === true)) {
|
|
29
|
+
PluginController.getInstance(pluginId, standaloneDetected).then(setPlugin);
|
|
30
|
+
}
|
|
31
|
+
}, [pluginId, standaloneClient]);
|
|
21
32
|
|
|
22
33
|
//route change
|
|
23
34
|
useEffect(() => {
|
|
@@ -50,6 +61,12 @@ export const PluginProvider: React.FC<PluginProviderProps> = ({ children, plugin
|
|
|
50
61
|
return () => body.removeEventListener('resize', handleResize);
|
|
51
62
|
}, [plugin]);
|
|
52
63
|
|
|
64
|
+
if (standaloneClient instanceof StandaloneClient) {
|
|
65
|
+
return <StandaloneAuth onLogin={async (email, password) => {
|
|
66
|
+
if (await standaloneClient.login(email, password)) setStandaloneClient(true);
|
|
67
|
+
}} />
|
|
68
|
+
}
|
|
69
|
+
|
|
53
70
|
if (!plugin) {
|
|
54
71
|
return ""
|
|
55
72
|
}
|
|
@@ -74,4 +91,32 @@ function initEventBus(pluginId: string) {
|
|
|
74
91
|
const url = new URL(window.location.href);
|
|
75
92
|
const isSidebar = url.searchParams.get("applicationMode") === "sidebar";
|
|
76
93
|
EventBusHandler.getInstance("Plugin EventBus " + pluginId + " " + (isSidebar ? "sidebar" : "main"));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function StandaloneAuth({ onLogin }: { onLogin: (user: string, password: string) => void }) {
|
|
97
|
+
const [user, setUser] = useState("");
|
|
98
|
+
const [password, setPassword] = useState("");
|
|
99
|
+
return (
|
|
100
|
+
<div style={{
|
|
101
|
+
position: 'fixed',
|
|
102
|
+
inset: 0,
|
|
103
|
+
display: 'flex',
|
|
104
|
+
alignItems: 'center',
|
|
105
|
+
justifyContent: 'center',
|
|
106
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
107
|
+
}}>
|
|
108
|
+
<div style={{ backgroundColor: '#343534', padding: '1rem', borderRadius: '0.5rem', width: '500px', flexDirection: 'column', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
109
|
+
<p style={{ fontSize: '2rem', fontWeight: 'bold', marginBottom: '1rem', textAlign: 'center' }}>Rimori Login</p>
|
|
110
|
+
<p style={{ marginBottom: '1rem', textAlign: 'center' }}>
|
|
111
|
+
Please login with your Rimori developer account for this plugin to be able to access the Rimori platform the same it will operate in the Rimori platform.
|
|
112
|
+
</p>
|
|
113
|
+
{/* email and password input */}
|
|
114
|
+
<input style={{ marginBottom: '1rem', width: '100%', padding: '0.5rem', borderRadius: '0.5rem', border: 'none', backgroundColor: '#444444' }} type="email" placeholder="Email" onChange={(e) => setUser(e.target.value)} />
|
|
115
|
+
<input style={{ marginBottom: '1rem', width: '100%', padding: '0.5rem', borderRadius: '0.5rem', border: 'none', backgroundColor: '#444444' }} type="password" placeholder="Password" onChange={(e) => setPassword(e.target.value)} />
|
|
116
|
+
<button style={{ marginBottom: '1rem', width: '100%', padding: '0.5rem', borderRadius: '0.5rem', border: 'none', backgroundColor: '#928358' }} onClick={() => {
|
|
117
|
+
onLogin(user, password);
|
|
118
|
+
}}>Login</button>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
)
|
|
77
122
|
}
|