@qidcloud/sdk 1.1.1 → 1.2.1
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/README.md +34 -276
- package/dist/components/QidSignInButton.d.ts +15 -0
- package/dist/hooks/useQidAuth.d.ts +18 -0
- package/dist/index.d.ts +24 -4
- package/dist/index.js +2432 -5
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2425 -0
- package/dist/index.mjs.map +1 -0
- package/dist/modules/auth.d.ts +23 -0
- package/dist/modules/db.d.ts +21 -0
- package/dist/modules/edge.d.ts +61 -0
- package/dist/modules/logs.d.ts +20 -0
- package/dist/modules/vault.d.ts +57 -0
- package/dist/types.d.ts +46 -0
- package/package.json +34 -30
- package/rollup.config.mjs +26 -0
- package/src/components/QidSignInButton.tsx +174 -0
- package/src/hooks/useQidAuth.ts +104 -0
- package/src/index.ts +54 -4
- package/src/modules/auth.ts +77 -0
- package/src/modules/db.ts +40 -0
- package/src/modules/edge.ts +98 -0
- package/src/modules/logs.ts +30 -0
- package/src/modules/vault.ts +124 -0
- package/src/types.ts +52 -0
- package/tsconfig.json +30 -19
- package/dist/QidCloud.d.ts +0 -31
- package/dist/QidCloud.js +0 -142
- package/dist/crypto/pqc.d.ts +0 -12
- package/dist/crypto/pqc.js +0 -91
- package/dist/storage/index.d.ts +0 -18
- package/dist/storage/index.js +0 -58
- package/dist/utils.d.ts +0 -5
- package/dist/utils.js +0 -46
- package/src/QidCloud.ts +0 -171
- package/src/crypto/pqc.ts +0 -79
- package/src/storage/index.ts +0 -49
- package/src/utils.ts +0 -48
- package/verify_sdk.ts +0 -53
package/package.json
CHANGED
|
@@ -1,31 +1,35 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@qidcloud/sdk",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"scripts": {
|
|
9
|
-
"build": "
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"@types/
|
|
29
|
-
"
|
|
30
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@qidcloud/sdk",
|
|
3
|
+
"version": "1.2.1",
|
|
4
|
+
"description": "The official JavaScript/TypeScript SDK for QidCloud Identity and Enclave Services.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "rollup -c",
|
|
10
|
+
"watch": "rollup -c -w",
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [],
|
|
14
|
+
"author": "",
|
|
15
|
+
"license": "ISC",
|
|
16
|
+
"type": "commonjs",
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"axios": "^1.13.5",
|
|
19
|
+
"qrcode.react": "^4.2.0",
|
|
20
|
+
"socket.io-client": "^4.8.3"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@rollup/plugin-commonjs": "^29.0.0",
|
|
24
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
25
|
+
"@rollup/plugin-typescript": "^12.3.0",
|
|
26
|
+
"@types/node": "^25.2.3",
|
|
27
|
+
"@types/react": "^19.2.13",
|
|
28
|
+
"@types/react-dom": "^19.2.3",
|
|
29
|
+
"react": "^19.2.4",
|
|
30
|
+
"react-dom": "^19.2.4",
|
|
31
|
+
"rollup": "^4.57.1",
|
|
32
|
+
"tslib": "^2.8.1",
|
|
33
|
+
"typescript": "^5.9.3"
|
|
34
|
+
}
|
|
31
35
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import typescript from '@rollup/plugin-typescript';
|
|
2
|
+
import resolve from '@rollup/plugin-node-resolve';
|
|
3
|
+
import commonjs from '@rollup/plugin-commonjs';
|
|
4
|
+
import pkg from './package.json' with { type: 'json' };
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
input: 'src/index.ts',
|
|
8
|
+
output: [
|
|
9
|
+
{
|
|
10
|
+
file: pkg.main,
|
|
11
|
+
format: 'cjs',
|
|
12
|
+
sourcemap: true,
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
file: pkg.module,
|
|
16
|
+
format: 'es',
|
|
17
|
+
sourcemap: true,
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
external: [...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.peerDependencies || {})],
|
|
21
|
+
plugins: [
|
|
22
|
+
resolve(),
|
|
23
|
+
commonjs(),
|
|
24
|
+
typescript({ tsconfig: './tsconfig.json' }),
|
|
25
|
+
],
|
|
26
|
+
};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { QRCodeSVG } from 'qrcode.react';
|
|
3
|
+
import { QidCloud } from '../index';
|
|
4
|
+
import { QidUser } from '../types';
|
|
5
|
+
import { useQidAuth } from '../hooks/useQidAuth';
|
|
6
|
+
|
|
7
|
+
interface QidSignInButtonProps {
|
|
8
|
+
sdk: QidCloud;
|
|
9
|
+
onSuccess: (user: QidUser, token: string) => void;
|
|
10
|
+
onError?: (error: string) => void;
|
|
11
|
+
className?: string;
|
|
12
|
+
buttonText?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A ready-to-use React component for QidCloud QR identity authentication.
|
|
17
|
+
*/
|
|
18
|
+
export const QidSignInButton: React.FC<QidSignInButtonProps> = ({
|
|
19
|
+
sdk,
|
|
20
|
+
onSuccess,
|
|
21
|
+
onError,
|
|
22
|
+
className,
|
|
23
|
+
buttonText = 'Login with QidCloud'
|
|
24
|
+
}: QidSignInButtonProps) => {
|
|
25
|
+
const {
|
|
26
|
+
user,
|
|
27
|
+
token,
|
|
28
|
+
error,
|
|
29
|
+
session,
|
|
30
|
+
initializing,
|
|
31
|
+
login,
|
|
32
|
+
cancel
|
|
33
|
+
} = useQidAuth(sdk);
|
|
34
|
+
|
|
35
|
+
// Watch for success
|
|
36
|
+
React.useEffect(() => {
|
|
37
|
+
if (user && token) {
|
|
38
|
+
onSuccess(user, token);
|
|
39
|
+
}
|
|
40
|
+
}, [user, token, onSuccess]);
|
|
41
|
+
|
|
42
|
+
// Watch for errors
|
|
43
|
+
React.useEffect(() => {
|
|
44
|
+
if (error && onError) {
|
|
45
|
+
onError(error);
|
|
46
|
+
}
|
|
47
|
+
}, [error, onError]);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<>
|
|
51
|
+
<button
|
|
52
|
+
onClick={login}
|
|
53
|
+
disabled={initializing || !!session}
|
|
54
|
+
className={className || 'qid-signin-btn'}
|
|
55
|
+
style={{
|
|
56
|
+
backgroundColor: '#00e5ff',
|
|
57
|
+
color: '#000',
|
|
58
|
+
padding: '12px 24px',
|
|
59
|
+
borderRadius: '12px',
|
|
60
|
+
border: 'none',
|
|
61
|
+
fontWeight: '900',
|
|
62
|
+
cursor: (initializing || !!session) ? 'not-allowed' : 'pointer',
|
|
63
|
+
display: 'flex',
|
|
64
|
+
alignItems: 'center',
|
|
65
|
+
gap: '10px',
|
|
66
|
+
textTransform: 'uppercase',
|
|
67
|
+
fontSize: '0.85rem',
|
|
68
|
+
letterSpacing: '0.5px',
|
|
69
|
+
boxShadow: '0 4px 15px rgba(0, 229, 255, 0.3)',
|
|
70
|
+
transition: 'all 0.2s'
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
<div style={{
|
|
74
|
+
width: '8px',
|
|
75
|
+
height: '8px',
|
|
76
|
+
borderRadius: '50%',
|
|
77
|
+
backgroundColor: (initializing || !!session) ? '#333' : '#000',
|
|
78
|
+
boxShadow: (initializing || !!session) ? 'none' : '0 0 8px rgba(0,0,0,0.5)'
|
|
79
|
+
}} />
|
|
80
|
+
{initializing ? 'Preparing Handshake...' : (session ? 'Awaiting Scan...' : buttonText)}
|
|
81
|
+
</button>
|
|
82
|
+
|
|
83
|
+
{session && (
|
|
84
|
+
<div className="qid-modal-overlay" style={{
|
|
85
|
+
position: 'fixed',
|
|
86
|
+
top: 0, left: 0, right: 0, bottom: 0,
|
|
87
|
+
backgroundColor: 'rgba(0,0,0,0.92)',
|
|
88
|
+
display: 'flex',
|
|
89
|
+
justifyContent: 'center',
|
|
90
|
+
alignItems: 'center',
|
|
91
|
+
zIndex: 9999,
|
|
92
|
+
backdropFilter: 'blur(8px)'
|
|
93
|
+
}}>
|
|
94
|
+
<div className="qid-modal-content" style={{
|
|
95
|
+
backgroundColor: '#0a0a0a',
|
|
96
|
+
padding: '40px',
|
|
97
|
+
borderRadius: '28px',
|
|
98
|
+
textAlign: 'center',
|
|
99
|
+
color: '#fff',
|
|
100
|
+
maxWidth: '420px',
|
|
101
|
+
width: '90%',
|
|
102
|
+
border: '1px solid rgba(0, 229, 255, 0.2)',
|
|
103
|
+
boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.5)'
|
|
104
|
+
}}>
|
|
105
|
+
<div style={{ marginBottom: '25px' }}>
|
|
106
|
+
<div style={{
|
|
107
|
+
display: 'inline-block',
|
|
108
|
+
padding: '12px 20px',
|
|
109
|
+
borderRadius: '30px',
|
|
110
|
+
backgroundColor: 'rgba(0, 229, 255, 0.1)',
|
|
111
|
+
border: '1px solid rgba(0, 229, 255, 0.3)',
|
|
112
|
+
marginBottom: '15px'
|
|
113
|
+
}}>
|
|
114
|
+
<span style={{ color: '#00e5ff', fontSize: '0.7rem', fontWeight: '900', letterSpacing: '2px' }}>PQC ENCLAVE SECURED</span>
|
|
115
|
+
</div>
|
|
116
|
+
<h2 style={{ fontSize: '1.75rem', fontWeight: '900', marginBottom: '10px', letterSpacing: '-0.5px' }}>Identity Handshake</h2>
|
|
117
|
+
<p style={{ fontSize: '0.95rem', color: '#94a3b8', lineHeight: '1.6' }}>
|
|
118
|
+
Scan this encrypted gateway with your QidCloud app to authorize access.
|
|
119
|
+
</p>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<div style={{
|
|
123
|
+
backgroundColor: '#fff',
|
|
124
|
+
padding: '24px',
|
|
125
|
+
display: 'inline-block',
|
|
126
|
+
borderRadius: '24px',
|
|
127
|
+
marginBottom: '30px',
|
|
128
|
+
boxShadow: '0 10px 40px rgba(0,0,0,0.3)'
|
|
129
|
+
}}>
|
|
130
|
+
<QRCodeSVG
|
|
131
|
+
value={session.qrData}
|
|
132
|
+
size={220}
|
|
133
|
+
level="H"
|
|
134
|
+
includeMargin={false}
|
|
135
|
+
imageSettings={{
|
|
136
|
+
src: "https://api.qidcloud.com/favicon.ico",
|
|
137
|
+
x: undefined,
|
|
138
|
+
y: undefined,
|
|
139
|
+
height: 40,
|
|
140
|
+
width: 40,
|
|
141
|
+
excavate: true,
|
|
142
|
+
}}
|
|
143
|
+
/>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '15px' }}>
|
|
147
|
+
<div style={{ height: '4px', width: '60px', backgroundColor: '#00e5ff', margin: '0 auto', borderRadius: '2px', opacity: 0.5 }} />
|
|
148
|
+
|
|
149
|
+
<button onClick={cancel} style={{
|
|
150
|
+
display: 'block',
|
|
151
|
+
width: '100%',
|
|
152
|
+
padding: '16px',
|
|
153
|
+
backgroundColor: 'transparent',
|
|
154
|
+
color: '#64748b',
|
|
155
|
+
border: '1px solid rgba(148, 163, 184, 0.2)',
|
|
156
|
+
borderRadius: '16px',
|
|
157
|
+
cursor: 'pointer',
|
|
158
|
+
fontWeight: '700',
|
|
159
|
+
transition: 'all 0.2s',
|
|
160
|
+
fontSize: '0.9rem'
|
|
161
|
+
}}>
|
|
162
|
+
Cancel Handshake
|
|
163
|
+
</button>
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<div style={{ marginTop: '25px', fontSize: '0.75rem', color: '#475569' }}>
|
|
167
|
+
Session ID: {session.sessionId.slice(0, 8)}...
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
)}
|
|
172
|
+
</>
|
|
173
|
+
);
|
|
174
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { useState, useCallback, useEffect, useRef } from 'react';
|
|
2
|
+
import { QidCloud } from '../index';
|
|
3
|
+
import { QidUser, QidAuthSession } from '../types';
|
|
4
|
+
|
|
5
|
+
export interface UseQidAuthReturn {
|
|
6
|
+
user: QidUser | null;
|
|
7
|
+
token: string | null;
|
|
8
|
+
loading: boolean;
|
|
9
|
+
error: string | null;
|
|
10
|
+
session: QidAuthSession | null;
|
|
11
|
+
initializing: boolean;
|
|
12
|
+
login: () => Promise<void>;
|
|
13
|
+
logout: () => void;
|
|
14
|
+
cancel: () => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A React hook for managing QidCloud authentication lifecycle.
|
|
19
|
+
* Handles handshake initialization, WebSocket listeners, and profile fetching.
|
|
20
|
+
*/
|
|
21
|
+
export function useQidAuth(sdk: QidCloud): UseQidAuthReturn {
|
|
22
|
+
const [user, setUser] = useState<QidUser | null>(null);
|
|
23
|
+
const [token, setToken] = useState<string | null>(null);
|
|
24
|
+
const [session, setSession] = useState<QidAuthSession | null>(null);
|
|
25
|
+
const [loading, setLoading] = useState(false);
|
|
26
|
+
const [initializing, setInitializing] = useState(false);
|
|
27
|
+
const [error, setError] = useState<string | null>(null);
|
|
28
|
+
|
|
29
|
+
// Use ref to track if we should still be listening (to avoid state updates after unmount/cancel)
|
|
30
|
+
const activeSessionId = useRef<string | null>(null);
|
|
31
|
+
|
|
32
|
+
const logout = useCallback(() => {
|
|
33
|
+
setUser(null);
|
|
34
|
+
setToken(null);
|
|
35
|
+
setSession(null);
|
|
36
|
+
sdk.auth.disconnect();
|
|
37
|
+
}, [sdk]);
|
|
38
|
+
|
|
39
|
+
const cancel = useCallback(() => {
|
|
40
|
+
activeSessionId.current = null;
|
|
41
|
+
setSession(null);
|
|
42
|
+
setInitializing(false);
|
|
43
|
+
sdk.auth.disconnect();
|
|
44
|
+
}, [sdk]);
|
|
45
|
+
|
|
46
|
+
const login = useCallback(async () => {
|
|
47
|
+
setInitializing(true);
|
|
48
|
+
setError(null);
|
|
49
|
+
try {
|
|
50
|
+
const newSession = await sdk.auth.createSession();
|
|
51
|
+
setSession(newSession);
|
|
52
|
+
activeSessionId.current = newSession.sessionId;
|
|
53
|
+
|
|
54
|
+
sdk.auth.listen(
|
|
55
|
+
newSession.sessionId,
|
|
56
|
+
async (receivedToken: string) => {
|
|
57
|
+
// Safety check: is this still the active session?
|
|
58
|
+
if (activeSessionId.current !== newSession.sessionId) return;
|
|
59
|
+
|
|
60
|
+
setLoading(true);
|
|
61
|
+
setInitializing(false);
|
|
62
|
+
try {
|
|
63
|
+
const profile = await sdk.auth.getProfile(receivedToken);
|
|
64
|
+
setToken(receivedToken);
|
|
65
|
+
setUser(profile);
|
|
66
|
+
} catch (err: any) {
|
|
67
|
+
setError(err.message || 'Failed to fetch user profile');
|
|
68
|
+
} finally {
|
|
69
|
+
setLoading(false);
|
|
70
|
+
setSession(null);
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
(err: string) => {
|
|
74
|
+
if (activeSessionId.current !== newSession.sessionId) return;
|
|
75
|
+
setError(err);
|
|
76
|
+
setInitializing(false);
|
|
77
|
+
setSession(null);
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
} catch (err: any) {
|
|
81
|
+
setError(err.message || 'Failed to initiate login handshake');
|
|
82
|
+
setInitializing(false);
|
|
83
|
+
}
|
|
84
|
+
}, [sdk]);
|
|
85
|
+
|
|
86
|
+
// Cleanup on unmount
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
return () => {
|
|
89
|
+
sdk.auth.disconnect();
|
|
90
|
+
};
|
|
91
|
+
}, [sdk]);
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
user,
|
|
95
|
+
token,
|
|
96
|
+
loading,
|
|
97
|
+
error,
|
|
98
|
+
session,
|
|
99
|
+
initializing,
|
|
100
|
+
login,
|
|
101
|
+
logout,
|
|
102
|
+
cancel
|
|
103
|
+
};
|
|
104
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,57 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
1
|
+
import axios, { AxiosInstance } from 'axios';
|
|
2
|
+
import { QidConfig } from './types';
|
|
3
|
+
import { AuthModule } from './modules/auth';
|
|
4
|
+
import { DbModule } from './modules/db';
|
|
5
|
+
import { EdgeModule } from './modules/edge';
|
|
6
|
+
import { VaultModule } from './modules/vault';
|
|
7
|
+
import { LogsModule } from './modules/logs';
|
|
4
8
|
|
|
5
|
-
export
|
|
9
|
+
export class QidCloud {
|
|
10
|
+
private config: QidConfig;
|
|
11
|
+
public api: AxiosInstance;
|
|
12
|
+
|
|
13
|
+
public auth: AuthModule;
|
|
14
|
+
public db: DbModule;
|
|
15
|
+
public edge: EdgeModule;
|
|
16
|
+
public vault: VaultModule;
|
|
17
|
+
public logs: LogsModule;
|
|
18
|
+
|
|
19
|
+
constructor(config: QidConfig) {
|
|
20
|
+
this.config = {
|
|
21
|
+
baseUrl: 'https://api.qidcloud.com',
|
|
22
|
+
...config,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
this.api = axios.create({
|
|
26
|
+
baseURL: this.config.baseUrl,
|
|
27
|
+
headers: {
|
|
28
|
+
'x-api-key': this.config.apiKey,
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
this.auth = new AuthModule(this);
|
|
34
|
+
this.db = new DbModule(this);
|
|
35
|
+
this.edge = new EdgeModule(this);
|
|
36
|
+
this.vault = new VaultModule(this);
|
|
37
|
+
this.logs = new LogsModule(this);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get the current project configuration
|
|
42
|
+
*/
|
|
43
|
+
getConfig(): QidConfig {
|
|
44
|
+
return { ...this.config };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Modules will be initialized here
|
|
48
|
+
// public auth = new AuthModule(this);
|
|
49
|
+
// public db = new DbModule(this);
|
|
50
|
+
// public storage = new StorageModule(this);
|
|
51
|
+
// public edge = new EdgeModule(this);
|
|
52
|
+
}
|
|
6
53
|
|
|
7
54
|
export default QidCloud;
|
|
55
|
+
export * from './types';
|
|
56
|
+
export { QidSignInButton } from './components/QidSignInButton';
|
|
57
|
+
export { useQidAuth } from './hooks/useQidAuth';
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { io, Socket } from 'socket.io-client';
|
|
2
|
+
import { QidCloud } from '../index';
|
|
3
|
+
import { QidAuthSession, QidUser } from '../types';
|
|
4
|
+
|
|
5
|
+
export class AuthModule {
|
|
6
|
+
private sdk: QidCloud;
|
|
7
|
+
private socket: Socket | null = null;
|
|
8
|
+
|
|
9
|
+
constructor(sdk: QidCloud) {
|
|
10
|
+
this.sdk = sdk;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Initialize a new handshake session for QR login
|
|
15
|
+
*/
|
|
16
|
+
async createSession(): Promise<QidAuthSession> {
|
|
17
|
+
const resp = await this.sdk.api.post('/api/initiate-generic');
|
|
18
|
+
const { sessionId, nonce } = resp.data;
|
|
19
|
+
|
|
20
|
+
// Construct QR data
|
|
21
|
+
const qrData = `qid: handshake:${sessionId}:${nonce} `;
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
sessionId,
|
|
25
|
+
qrData,
|
|
26
|
+
expiresAt: Date.now() + 120000 // 2 minutes
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Listen for authorization events for a specific session
|
|
32
|
+
*/
|
|
33
|
+
listen(sessionId: string, onAuthorized: (token: string) => void, onDenied?: (msg: string) => void) {
|
|
34
|
+
if (!this.socket) {
|
|
35
|
+
const baseUrl = this.sdk.getConfig().baseUrl || 'https://api.qidcloud.com';
|
|
36
|
+
this.socket = io(baseUrl);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this.socket.emit('join', sessionId);
|
|
40
|
+
|
|
41
|
+
this.socket.on('authenticated', (token: string) => {
|
|
42
|
+
onAuthorized(token);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
this.socket.on('identity_authorized', (data: any) => {
|
|
46
|
+
// Some backend routes wrap the token in an object
|
|
47
|
+
const token = typeof data === 'string' ? data : data.token;
|
|
48
|
+
onAuthorized(token);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
this.socket.on('identity_denied', (data: any) => {
|
|
52
|
+
if (onDenied) onDenied(data.message || 'Authorization denied');
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Stop listening and disconnect socket
|
|
58
|
+
*/
|
|
59
|
+
disconnect() {
|
|
60
|
+
if (this.socket) {
|
|
61
|
+
this.socket.disconnect();
|
|
62
|
+
this.socket = null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Fetch user profile using a session token
|
|
68
|
+
*/
|
|
69
|
+
async getProfile(token: string): Promise<QidUser> {
|
|
70
|
+
const resp = await this.sdk.api.get('/api/me', {
|
|
71
|
+
headers: {
|
|
72
|
+
'Authorization': `Bearer ${token}`
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
return resp.data;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { QidCloud } from '../index';
|
|
2
|
+
import { QidDbResponse } from '../types';
|
|
3
|
+
|
|
4
|
+
export class DbModule {
|
|
5
|
+
private sdk: QidCloud;
|
|
6
|
+
|
|
7
|
+
constructor(sdk: QidCloud) {
|
|
8
|
+
this.sdk = sdk;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Execute a SQL query against the project's enclave database
|
|
13
|
+
* @param sql The SQL query string
|
|
14
|
+
* @param params Parameterized values for the query
|
|
15
|
+
* @param userToken Optional session token for user-scoped access
|
|
16
|
+
*/
|
|
17
|
+
async query<T = any>(sql: string, params: any[] = [], userToken?: string): Promise<QidDbResponse<T>> {
|
|
18
|
+
const headers: any = {};
|
|
19
|
+
if (userToken) {
|
|
20
|
+
headers['Authorization'] = `Bearer ${userToken}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const resp = await this.sdk.api.post('/api/db/query', { sql, params }, { headers });
|
|
24
|
+
return resp.data;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Initialize the project's enclave database schema
|
|
29
|
+
* Useful for first-time project setup
|
|
30
|
+
*/
|
|
31
|
+
async setup(userToken?: string): Promise<{ success: boolean; schemaName: string }> {
|
|
32
|
+
const headers: any = {};
|
|
33
|
+
if (userToken) {
|
|
34
|
+
headers['Authorization'] = `Bearer ${userToken}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const resp = await this.sdk.api.post('/api/db/setup', {}, { headers });
|
|
38
|
+
return resp.data;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { QidCloud } from '../index';
|
|
2
|
+
import { QidEdgeResponse } from '../types';
|
|
3
|
+
|
|
4
|
+
export class EdgeModule {
|
|
5
|
+
private sdk: QidCloud;
|
|
6
|
+
|
|
7
|
+
constructor(sdk: QidCloud) {
|
|
8
|
+
this.sdk = sdk;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Invoke a serverless edge function
|
|
13
|
+
* @param name Name of the function to invoke
|
|
14
|
+
* @param params Arguments passed to the function
|
|
15
|
+
* @param userToken Optional session token for user-scoped access
|
|
16
|
+
*/
|
|
17
|
+
async invoke(name: string, params: any = {}, userToken?: string): Promise<QidEdgeResponse> {
|
|
18
|
+
const headers: any = {};
|
|
19
|
+
if (userToken) {
|
|
20
|
+
headers['Authorization'] = `Bearer ${userToken}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const resp = await this.sdk.api.post('/api/edge/invoke', { name, params }, { headers });
|
|
24
|
+
return resp.data;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* List all functions in the project enclave
|
|
29
|
+
*/
|
|
30
|
+
async list(): Promise<any[]> {
|
|
31
|
+
const resp = await this.sdk.api.get('/api/edge/list');
|
|
32
|
+
return resp.data;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get logs for a specific function
|
|
37
|
+
* @param name Function name
|
|
38
|
+
* @param userToken Optional session token
|
|
39
|
+
*/
|
|
40
|
+
async getLogs(name: string, userToken?: string): Promise<any[]> {
|
|
41
|
+
const headers: any = {};
|
|
42
|
+
if (userToken) headers['Authorization'] = `Bearer ${userToken}`;
|
|
43
|
+
const resp = await this.sdk.api.get(`/api/edge/${name}/logs`, { headers });
|
|
44
|
+
return resp.data;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get project-wide enclave logs
|
|
49
|
+
*/
|
|
50
|
+
async getProjectLogs(userToken?: string): Promise<any[]> {
|
|
51
|
+
const headers: any = {};
|
|
52
|
+
if (userToken) headers['Authorization'] = `Bearer ${userToken}`;
|
|
53
|
+
const resp = await this.sdk.api.get('/api/edge/logs/project', { headers });
|
|
54
|
+
return resp.data;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Delete a function from the enclave
|
|
59
|
+
*/
|
|
60
|
+
async delete(name: string, userToken?: string): Promise<{ success: boolean }> {
|
|
61
|
+
const headers: any = {};
|
|
62
|
+
if (userToken) headers['Authorization'] = `Bearer ${userToken}`;
|
|
63
|
+
const resp = await this.sdk.api.delete(`/api/edge/${name}`, { headers });
|
|
64
|
+
return resp.data;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Deploy a new function or update an existing one
|
|
69
|
+
* @param data Deployment data including name, code, and optional envVars
|
|
70
|
+
* @param userToken Optional session token
|
|
71
|
+
*/
|
|
72
|
+
async deploy(data: { name: string; code: string; runtime?: string; envVars?: any; overwrite?: boolean }, userToken?: string): Promise<{ success: boolean; message: string }> {
|
|
73
|
+
const headers: any = {};
|
|
74
|
+
if (userToken) headers['Authorization'] = `Bearer ${userToken}`;
|
|
75
|
+
const resp = await this.sdk.api.post('/api/edge/deploy', data, { headers });
|
|
76
|
+
return resp.data;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get available serverless runtimes
|
|
81
|
+
*/
|
|
82
|
+
async getRuntimes(): Promise<string[]> {
|
|
83
|
+
const resp = await this.sdk.api.get('/api/edge/runtimes');
|
|
84
|
+
return resp.data;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Toggle centralized logging for the project's edge functions
|
|
89
|
+
* @param enabled Whether to enable or disable logging
|
|
90
|
+
* @param userToken Optional session token
|
|
91
|
+
*/
|
|
92
|
+
async toggleLogging(enabled: boolean, userToken?: string): Promise<{ success: boolean; loggingEnabled: boolean }> {
|
|
93
|
+
const headers: any = {};
|
|
94
|
+
if (userToken) headers['Authorization'] = `Bearer ${userToken}`;
|
|
95
|
+
const resp = await this.sdk.api.post('/api/edge/logs/settings', { enabled }, { headers });
|
|
96
|
+
return resp.data;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { QidCloud } from '../index';
|
|
2
|
+
|
|
3
|
+
export class LogsModule {
|
|
4
|
+
private sdk: QidCloud;
|
|
5
|
+
|
|
6
|
+
constructor(sdk: QidCloud) {
|
|
7
|
+
this.sdk = sdk;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Write a custom application log to the project enclave
|
|
12
|
+
* @param message The log message
|
|
13
|
+
* @param source The source of the log (e.g. 'auth-service', 'frontend')
|
|
14
|
+
* @param level Log level (info, warn, error)
|
|
15
|
+
* @param metadata Structured data for searching/filtering
|
|
16
|
+
*/
|
|
17
|
+
async write(message: string, source: string = 'app', level: string = 'info', metadata: any = {}): Promise<{ success: boolean }> {
|
|
18
|
+
const resp = await this.sdk.api.post('/api/logs', { message, source, level, metadata });
|
|
19
|
+
return resp.data;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Retrieve application logs for the project
|
|
24
|
+
* @param query Filters (limit, level, etc.)
|
|
25
|
+
*/
|
|
26
|
+
async fetch(query: any = {}): Promise<any[]> {
|
|
27
|
+
const resp = await this.sdk.api.get('/api/logs', { params: query });
|
|
28
|
+
return resp.data;
|
|
29
|
+
}
|
|
30
|
+
}
|