@joonweb/joonweb-sdk 1.0.0
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/index.js +8 -0
- package/package.json +60 -0
- package/src/Auth/Auth.js +193 -0
- package/src/Auth/OAuth.js +135 -0
- package/src/Auth/SessionManager.js +145 -0
- package/src/Auth/session/Session.js +46 -0
- package/src/Auth/session/StorageInterface.js +48 -0
- package/src/Auth/session/storage/MemoryStorage.js +34 -0
- package/src/Auth/session/storage/MongoDBStorage.js +107 -0
- package/src/Auth/session/storage/MySQLStorage.js +281 -0
- package/src/Auth/session/storage/PostgreSQLStorage.js +372 -0
- package/src/Auth/session/storage/RedisStorage.js +133 -0
- package/src/Auth/session/storage/SQLiteStorage.js +133 -0
- package/src/Auth/session/storage/index.js +46 -0
- package/src/Clients/BaseClient.js +66 -0
- package/src/Context.js +82 -0
- package/src/Helper.js +92 -0
- package/src/JoonWebAPI.js +81 -0
- package/src/Resources/BaseResource.js +32 -0
- package/src/Resources/Customer.js +34 -0
- package/src/Resources/Order.js +18 -0
- package/src/Resources/Product.js +30 -0
- package/src/Resources/Site.js +11 -0
- package/src/Resources/Theme.js +7 -0
- package/src/Resources/Webhook.js +30 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// src/Auth/session/storage/MongoDBStorage.js
|
|
2
|
+
let MongoClient;
|
|
3
|
+
|
|
4
|
+
try {
|
|
5
|
+
MongoClient = require('mongodb').MongoClient;
|
|
6
|
+
} catch (error) {
|
|
7
|
+
// mongodb not installed
|
|
8
|
+
MongoClient = null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class MongoDBStorage {
|
|
12
|
+
constructor(options = {}) {
|
|
13
|
+
if (!MongoClient) {
|
|
14
|
+
throw new Error('mongodb package not installed. Run: npm install mongodb');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
this.url = options.url || 'mongodb://localhost:27017';
|
|
18
|
+
this.dbName = options.dbName || 'joonweb';
|
|
19
|
+
this.collectionName = options.collectionName || 'sessions';
|
|
20
|
+
this.client = null;
|
|
21
|
+
this.collection = null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async initDatabase() {
|
|
25
|
+
this.client = new MongoClient(this.url);
|
|
26
|
+
await this.client.connect();
|
|
27
|
+
const db = this.client.db(this.dbName);
|
|
28
|
+
this.collection = db.collection(this.collectionName);
|
|
29
|
+
|
|
30
|
+
// Create TTL index for automatic expiry
|
|
31
|
+
await this.collection.createIndex(
|
|
32
|
+
{ expires_at: 1 },
|
|
33
|
+
{ expireAfterSeconds: 0 }
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// Create index on site_domain
|
|
37
|
+
await this.collection.createIndex({ site_domain: 1 }, { unique: true });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async ensureConnected() {
|
|
41
|
+
if (!this.collection) {
|
|
42
|
+
await this.initDatabase();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async store(siteDomain, session) {
|
|
47
|
+
await this.ensureConnected();
|
|
48
|
+
|
|
49
|
+
await this.collection.updateOne(
|
|
50
|
+
{ site_domain: siteDomain },
|
|
51
|
+
{
|
|
52
|
+
$set: {
|
|
53
|
+
...session,
|
|
54
|
+
updated_at: Date.now(),
|
|
55
|
+
_id: siteDomain // Use siteDomain as _id for uniqueness
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
{ upsert: true }
|
|
59
|
+
);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async load(siteDomain) {
|
|
64
|
+
await this.ensureConnected();
|
|
65
|
+
const session = await this.collection.findOne({ site_domain: siteDomain });
|
|
66
|
+
|
|
67
|
+
if (session) {
|
|
68
|
+
// Remove MongoDB _id field
|
|
69
|
+
delete session._id;
|
|
70
|
+
return session;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async delete(siteDomain) {
|
|
77
|
+
await this.ensureConnected();
|
|
78
|
+
const result = await this.collection.deleteOne({ site_domain: siteDomain });
|
|
79
|
+
return result.deletedCount > 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async list() {
|
|
83
|
+
await this.ensureConnected();
|
|
84
|
+
const sessions = await this.collection.find({}).toArray();
|
|
85
|
+
|
|
86
|
+
// Remove MongoDB _id field from all sessions
|
|
87
|
+
return sessions.map(session => {
|
|
88
|
+
const { _id, ...rest } = session;
|
|
89
|
+
return rest;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async clear() {
|
|
94
|
+
await this.ensureConnected();
|
|
95
|
+
await this.collection.deleteMany({});
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async close() {
|
|
100
|
+
if (this.client) {
|
|
101
|
+
await this.client.close();
|
|
102
|
+
}
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = MongoDBStorage;
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
// src/Auth/session/storage/MySQLStorage.js
|
|
2
|
+
let mysql;
|
|
3
|
+
|
|
4
|
+
try {
|
|
5
|
+
mysql = require('mysql2/promise');
|
|
6
|
+
} catch (error) {
|
|
7
|
+
// mysql2 not installed
|
|
8
|
+
mysql = null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class MySQLStorage {
|
|
12
|
+
constructor(options = {}) {
|
|
13
|
+
if (!mysql) {
|
|
14
|
+
throw new Error('mysql2 package not installed. Run: npm install mysql2');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
this.config = {
|
|
18
|
+
host: options.host || 'localhost',
|
|
19
|
+
port: options.port || 3306,
|
|
20
|
+
user: options.user || 'root',
|
|
21
|
+
password: options.password || '',
|
|
22
|
+
database: options.database || 'joonweb_sessions',
|
|
23
|
+
connectionLimit: options.connectionLimit || 10,
|
|
24
|
+
waitForConnections: true,
|
|
25
|
+
queueLimit: 0,
|
|
26
|
+
enableKeepAlive: true,
|
|
27
|
+
keepAliveInitialDelay: 0
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
this.tableName = options.tableName || 'sessions';
|
|
31
|
+
this.pool = null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async initDatabase() {
|
|
35
|
+
if (!this.pool) {
|
|
36
|
+
this.pool = mysql.createPool(this.config);
|
|
37
|
+
|
|
38
|
+
// Create table if not exists
|
|
39
|
+
await this.createTable();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async createTable() {
|
|
44
|
+
const connection = await this.pool.getConnection();
|
|
45
|
+
try {
|
|
46
|
+
await connection.query(`
|
|
47
|
+
CREATE TABLE IF NOT EXISTS \`${this.tableName}\` (
|
|
48
|
+
\`id\` VARCHAR(255) NOT NULL,
|
|
49
|
+
\`site_domain\` VARCHAR(255) NOT NULL,
|
|
50
|
+
\`access_token\` TEXT NOT NULL,
|
|
51
|
+
\`scope\` TEXT,
|
|
52
|
+
\`user_data\` JSON,
|
|
53
|
+
\`expires_at\` BIGINT,
|
|
54
|
+
\`authenticated_at\` BIGINT,
|
|
55
|
+
\`created_at\` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
56
|
+
\`updated_at\` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
57
|
+
\`is_online\` BOOLEAN DEFAULT FALSE,
|
|
58
|
+
\`online_access_info\` JSON,
|
|
59
|
+
\`state\` VARCHAR(255),
|
|
60
|
+
\`metadata\` JSON,
|
|
61
|
+
PRIMARY KEY (\`id\`),
|
|
62
|
+
UNIQUE KEY \`idx_site_domain\` (\`site_domain\`),
|
|
63
|
+
KEY \`idx_expires_at\` (\`expires_at\`),
|
|
64
|
+
KEY \`idx_created_at\` (\`created_at\`)
|
|
65
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
66
|
+
`);
|
|
67
|
+
|
|
68
|
+
// Add cleanup event for expired sessions
|
|
69
|
+
await this.cleanupExpiredSessions();
|
|
70
|
+
} finally {
|
|
71
|
+
connection.release();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async ensureConnected() {
|
|
76
|
+
if (!this.pool) {
|
|
77
|
+
await this.initDatabase();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async store(siteDomain, session) {
|
|
82
|
+
await this.ensureConnected();
|
|
83
|
+
|
|
84
|
+
const connection = await this.pool.getConnection();
|
|
85
|
+
try {
|
|
86
|
+
// Generate a unique ID for the session
|
|
87
|
+
const sessionId = require('crypto').randomBytes(16).toString('hex');
|
|
88
|
+
|
|
89
|
+
await connection.query(
|
|
90
|
+
`INSERT INTO \`${this.tableName}\`
|
|
91
|
+
(id, site_domain, access_token, scope, user_data, expires_at, authenticated_at, is_online, online_access_info, state, metadata)
|
|
92
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
93
|
+
ON DUPLICATE KEY UPDATE
|
|
94
|
+
access_token = VALUES(access_token),
|
|
95
|
+
scope = VALUES(scope),
|
|
96
|
+
user_data = VALUES(user_data),
|
|
97
|
+
expires_at = VALUES(expires_at),
|
|
98
|
+
is_online = VALUES(is_online),
|
|
99
|
+
online_access_info = VALUES(online_access_info),
|
|
100
|
+
state = VALUES(state),
|
|
101
|
+
metadata = VALUES(metadata),
|
|
102
|
+
updated_at = CURRENT_TIMESTAMP`,
|
|
103
|
+
[
|
|
104
|
+
sessionId,
|
|
105
|
+
siteDomain,
|
|
106
|
+
session.access_token,
|
|
107
|
+
session.scope,
|
|
108
|
+
JSON.stringify(session.user || {}),
|
|
109
|
+
session.expires_at,
|
|
110
|
+
session.authenticated_at,
|
|
111
|
+
session.is_online || false,
|
|
112
|
+
JSON.stringify(session.online_access_info || {}),
|
|
113
|
+
session.state || '',
|
|
114
|
+
JSON.stringify(session.metadata || {})
|
|
115
|
+
]
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
return true;
|
|
119
|
+
} finally {
|
|
120
|
+
connection.release();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async load(siteDomain) {
|
|
125
|
+
await this.ensureConnected();
|
|
126
|
+
|
|
127
|
+
const connection = await this.pool.getConnection();
|
|
128
|
+
try {
|
|
129
|
+
const [rows] = await connection.query(
|
|
130
|
+
`SELECT * FROM \`${this.tableName}\` WHERE site_domain = ? ORDER BY updated_at DESC LIMIT 1`,
|
|
131
|
+
[siteDomain]
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
if (rows.length === 0) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const row = rows[0];
|
|
139
|
+
|
|
140
|
+
// Parse JSON fields
|
|
141
|
+
const session = {
|
|
142
|
+
id: row.id,
|
|
143
|
+
site_domain: row.site_domain,
|
|
144
|
+
access_token: row.access_token,
|
|
145
|
+
scope: row.scope,
|
|
146
|
+
user: row.user_data ? JSON.parse(row.user_data) : null,
|
|
147
|
+
expires_at: row.expires_at,
|
|
148
|
+
authenticated_at: row.authenticated_at,
|
|
149
|
+
is_online: Boolean(row.is_online),
|
|
150
|
+
online_access_info: row.online_access_info ? JSON.parse(row.online_access_info) : {},
|
|
151
|
+
state: row.state,
|
|
152
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : {},
|
|
153
|
+
created_at: row.created_at,
|
|
154
|
+
updated_at: row.updated_at
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
return session;
|
|
158
|
+
} finally {
|
|
159
|
+
connection.release();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async delete(siteDomain) {
|
|
164
|
+
await this.ensureConnected();
|
|
165
|
+
|
|
166
|
+
const connection = await this.pool.getConnection();
|
|
167
|
+
try {
|
|
168
|
+
const [result] = await connection.query(
|
|
169
|
+
`DELETE FROM \`${this.tableName}\` WHERE site_domain = ?`,
|
|
170
|
+
[siteDomain]
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
return result.affectedRows > 0;
|
|
174
|
+
} finally {
|
|
175
|
+
connection.release();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async list() {
|
|
180
|
+
await this.ensureConnected();
|
|
181
|
+
|
|
182
|
+
const connection = await this.pool.getConnection();
|
|
183
|
+
try {
|
|
184
|
+
const [rows] = await connection.query(
|
|
185
|
+
`SELECT * FROM \`${this.tableName}\` ORDER BY updated_at DESC`
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
return rows.map(row => ({
|
|
189
|
+
id: row.id,
|
|
190
|
+
site_domain: row.site_domain,
|
|
191
|
+
access_token: row.access_token,
|
|
192
|
+
scope: row.scope,
|
|
193
|
+
user: row.user_data ? JSON.parse(row.user_data) : null,
|
|
194
|
+
expires_at: row.expires_at,
|
|
195
|
+
authenticated_at: row.authenticated_at,
|
|
196
|
+
is_online: Boolean(row.is_online),
|
|
197
|
+
online_access_info: row.online_access_info ? JSON.parse(row.online_access_info) : {},
|
|
198
|
+
state: row.state,
|
|
199
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : {},
|
|
200
|
+
created_at: row.created_at,
|
|
201
|
+
updated_at: row.updated_at
|
|
202
|
+
}));
|
|
203
|
+
} finally {
|
|
204
|
+
connection.release();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async clear() {
|
|
209
|
+
await this.ensureConnected();
|
|
210
|
+
|
|
211
|
+
const connection = await this.pool.getConnection();
|
|
212
|
+
try {
|
|
213
|
+
await connection.query(`TRUNCATE TABLE \`${this.tableName}\``);
|
|
214
|
+
return true;
|
|
215
|
+
} finally {
|
|
216
|
+
connection.release();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async close() {
|
|
221
|
+
if (this.pool) {
|
|
222
|
+
await this.pool.end();
|
|
223
|
+
}
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async cleanupExpiredSessions() {
|
|
228
|
+
await this.ensureConnected();
|
|
229
|
+
|
|
230
|
+
const connection = await this.pool.getConnection();
|
|
231
|
+
try {
|
|
232
|
+
const [result] = await connection.query(
|
|
233
|
+
`DELETE FROM \`${this.tableName}\` WHERE expires_at IS NOT NULL AND expires_at < ?`,
|
|
234
|
+
[Date.now()]
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
if (result.affectedRows > 0) {
|
|
238
|
+
console.log(`🧹 Cleaned up ${result.affectedRows} expired sessions from MySQL`);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return result.affectedRows;
|
|
242
|
+
} finally {
|
|
243
|
+
connection.release();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async getStats() {
|
|
248
|
+
await this.ensureConnected();
|
|
249
|
+
|
|
250
|
+
const connection = await this.pool.getConnection();
|
|
251
|
+
try {
|
|
252
|
+
const [rows] = await connection.query(`
|
|
253
|
+
SELECT
|
|
254
|
+
COUNT(*) as total_sessions,
|
|
255
|
+
COUNT(CASE WHEN expires_at IS NULL OR expires_at > ? THEN 1 END) as active_sessions,
|
|
256
|
+
COUNT(CASE WHEN is_online = TRUE THEN 1 END) as online_sessions,
|
|
257
|
+
MIN(created_at) as oldest_session,
|
|
258
|
+
MAX(updated_at) as latest_activity
|
|
259
|
+
FROM \`${this.tableName}\`
|
|
260
|
+
`, [Date.now()]);
|
|
261
|
+
|
|
262
|
+
return rows[0];
|
|
263
|
+
} finally {
|
|
264
|
+
connection.release();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async healthCheck() {
|
|
269
|
+
try {
|
|
270
|
+
await this.ensureConnected();
|
|
271
|
+
const connection = await this.pool.getConnection();
|
|
272
|
+
await connection.ping();
|
|
273
|
+
connection.release();
|
|
274
|
+
return { status: 'healthy', storage: 'mysql' };
|
|
275
|
+
} catch (error) {
|
|
276
|
+
return { status: 'unhealthy', storage: 'mysql', error: error.message };
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
module.exports = MySQLStorage;
|