@qidcloud/sdk 1.2.0 → 1.2.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/README.md +89 -100
- package/dist/components/QidSignInButton.d.ts +3 -0
- package/dist/hooks/useQidAuth.d.ts +18 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +237 -54
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +237 -55
- package/dist/index.mjs.map +1 -1
- package/dist/modules/edge.d.ts +28 -0
- package/dist/modules/vault.d.ts +24 -1
- package/package.json +34 -51
- package/rollup.config.mjs +26 -0
- package/src/components/QidSignInButton.tsx +174 -0
- package/src/hooks/useQidAuth.ts +104 -0
- package/src/index.ts +57 -0
- 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 +31 -0
package/dist/modules/edge.d.ts
CHANGED
|
@@ -30,4 +30,32 @@ export declare class EdgeModule {
|
|
|
30
30
|
delete(name: string, userToken?: string): Promise<{
|
|
31
31
|
success: boolean;
|
|
32
32
|
}>;
|
|
33
|
+
/**
|
|
34
|
+
* Deploy a new function or update an existing one
|
|
35
|
+
* @param data Deployment data including name, code, and optional envVars
|
|
36
|
+
* @param userToken Optional session token
|
|
37
|
+
*/
|
|
38
|
+
deploy(data: {
|
|
39
|
+
name: string;
|
|
40
|
+
code: string;
|
|
41
|
+
runtime?: string;
|
|
42
|
+
envVars?: any;
|
|
43
|
+
overwrite?: boolean;
|
|
44
|
+
}, userToken?: string): Promise<{
|
|
45
|
+
success: boolean;
|
|
46
|
+
message: string;
|
|
47
|
+
}>;
|
|
48
|
+
/**
|
|
49
|
+
* Get available serverless runtimes
|
|
50
|
+
*/
|
|
51
|
+
getRuntimes(): Promise<string[]>;
|
|
52
|
+
/**
|
|
53
|
+
* Toggle centralized logging for the project's edge functions
|
|
54
|
+
* @param enabled Whether to enable or disable logging
|
|
55
|
+
* @param userToken Optional session token
|
|
56
|
+
*/
|
|
57
|
+
toggleLogging(enabled: boolean, userToken?: string): Promise<{
|
|
58
|
+
success: boolean;
|
|
59
|
+
loggingEnabled: boolean;
|
|
60
|
+
}>;
|
|
33
61
|
}
|
package/dist/modules/vault.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export declare class VaultModule {
|
|
|
23
23
|
*/
|
|
24
24
|
download(fileId: string, userToken?: string): Promise<ArrayBuffer>;
|
|
25
25
|
/**
|
|
26
|
-
* Delete a file from storage
|
|
26
|
+
* Delete a file from storage (Soft Delete)
|
|
27
27
|
* @param fileId Unique ID of the file
|
|
28
28
|
* @param userToken Session token for user-scoped storage
|
|
29
29
|
*/
|
|
@@ -31,4 +31,27 @@ export declare class VaultModule {
|
|
|
31
31
|
success: boolean;
|
|
32
32
|
message: string;
|
|
33
33
|
}>;
|
|
34
|
+
/**
|
|
35
|
+
* List deleted files in the project enclave (Recycle Bin)
|
|
36
|
+
* @param userToken Session token for user-scoped storage
|
|
37
|
+
*/
|
|
38
|
+
listDeleted(userToken?: string): Promise<QidFile[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Restore a deleted file from the recycle bin
|
|
41
|
+
* @param fileId Unique ID of the file
|
|
42
|
+
* @param userToken Session token for user-scoped storage
|
|
43
|
+
*/
|
|
44
|
+
restore(fileId: string, userToken?: string): Promise<{
|
|
45
|
+
success: boolean;
|
|
46
|
+
message: string;
|
|
47
|
+
}>;
|
|
48
|
+
/**
|
|
49
|
+
* Permanently purge a deleted file
|
|
50
|
+
* @param fileId Unique ID of the file
|
|
51
|
+
* @param userToken Session token for user-scoped storage
|
|
52
|
+
*/
|
|
53
|
+
purge(fileId: string, userToken?: string): Promise<{
|
|
54
|
+
success: boolean;
|
|
55
|
+
message: string;
|
|
56
|
+
}>;
|
|
34
57
|
}
|
package/package.json
CHANGED
|
@@ -1,52 +1,35 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@qidcloud/sdk",
|
|
3
|
-
"version": "1.2.
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"dependencies": {
|
|
36
|
-
"axios": "^1.13.5",
|
|
37
|
-
"socket.io-client": "^4.8.3"
|
|
38
|
-
},
|
|
39
|
-
"devDependencies": {
|
|
40
|
-
"@rollup/plugin-commonjs": "^29.0.0",
|
|
41
|
-
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
42
|
-
"@rollup/plugin-typescript": "^12.3.0",
|
|
43
|
-
"@types/node": "^25.2.3",
|
|
44
|
-
"@types/react": "^19.2.13",
|
|
45
|
-
"@types/react-dom": "^19.2.3",
|
|
46
|
-
"react": "^19.2.4",
|
|
47
|
-
"react-dom": "^19.2.4",
|
|
48
|
-
"rollup": "^4.57.1",
|
|
49
|
-
"tslib": "^2.8.1",
|
|
50
|
-
"typescript": "^5.9.3"
|
|
51
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@qidcloud/sdk",
|
|
3
|
+
"version": "1.2.2",
|
|
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
|
+
}
|
|
52
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
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
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';
|
|
8
|
+
|
|
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
|
+
}
|
|
53
|
+
|
|
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
|
+
}
|