skill-base 2.0.14 → 2.0.16
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 +22 -0
- package/bin/skill-base.js +7 -0
- package/package.json +1 -1
- package/src/cappy.js +86 -33
- package/src/index.js +45 -32
- package/src/models/skill.js +53 -27
- package/src/models/user.js +27 -2
- package/src/models/version.js +39 -21
- package/src/routes/collaborators.js +5 -0
- package/src/routes/publish.js +2 -0
- package/src/utils/lru-cache.js +160 -0
- package/src/utils/model-cache.js +158 -0
- package/static/assets/{index-BTXGUfWd.js → index-BHB0vddE.js} +16 -16
- package/static/assets/{index-BYG48306.css → index-EVWfLxoq.css} +1 -1
- package/static/index.html +2 -2
package/src/models/user.js
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
const db = require('../database');
|
|
2
|
+
const modelCache = require('../utils/model-cache');
|
|
3
|
+
|
|
4
|
+
function queryById(id) {
|
|
5
|
+
return db.prepare('SELECT id, username, name, role, status, created_at, updated_at FROM users WHERE id = ?').get(id);
|
|
6
|
+
}
|
|
2
7
|
|
|
3
8
|
const UserModel = {
|
|
4
9
|
// 根据 ID 查询用户
|
|
5
10
|
findById(id) {
|
|
6
|
-
return
|
|
11
|
+
return modelCache.remember(
|
|
12
|
+
modelCache.keys.userBasic(id),
|
|
13
|
+
() => queryById(id),
|
|
14
|
+
modelCache.refs.user
|
|
15
|
+
);
|
|
7
16
|
},
|
|
8
17
|
|
|
9
18
|
// 根据用户名查询(含 password_hash,用于登录验证)
|
|
@@ -14,7 +23,8 @@ const UserModel = {
|
|
|
14
23
|
// 创建用户
|
|
15
24
|
create(username, passwordHash, role = 'developer') {
|
|
16
25
|
const result = db.prepare('INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)').run(username, passwordHash, role);
|
|
17
|
-
|
|
26
|
+
modelCache.invalidateUser(result.lastInsertRowid);
|
|
27
|
+
return queryById(result.lastInsertRowid);
|
|
18
28
|
},
|
|
19
29
|
|
|
20
30
|
// 列出用户(支持分页和搜索)
|
|
@@ -51,6 +61,9 @@ const UserModel = {
|
|
|
51
61
|
const result = db.prepare(
|
|
52
62
|
"UPDATE users SET username = ?, updated_at = datetime('now') WHERE id = ?"
|
|
53
63
|
).run(username, id);
|
|
64
|
+
if (result.changes > 0) {
|
|
65
|
+
modelCache.invalidateUser(id);
|
|
66
|
+
}
|
|
54
67
|
return result.changes > 0;
|
|
55
68
|
},
|
|
56
69
|
|
|
@@ -59,6 +72,9 @@ const UserModel = {
|
|
|
59
72
|
const result = db.prepare(
|
|
60
73
|
"UPDATE users SET password_hash = ?, updated_at = datetime('now') WHERE id = ?"
|
|
61
74
|
).run(passwordHash, id);
|
|
75
|
+
if (result.changes > 0) {
|
|
76
|
+
modelCache.invalidateUser(id);
|
|
77
|
+
}
|
|
62
78
|
return result.changes > 0;
|
|
63
79
|
},
|
|
64
80
|
|
|
@@ -81,6 +97,9 @@ const UserModel = {
|
|
|
81
97
|
params.push(id);
|
|
82
98
|
|
|
83
99
|
const result = db.prepare(`UPDATE users SET ${sets.join(', ')} WHERE id = ?`).run(...params);
|
|
100
|
+
if (result.changes > 0) {
|
|
101
|
+
modelCache.invalidateUser(id);
|
|
102
|
+
}
|
|
84
103
|
return result.changes > 0;
|
|
85
104
|
},
|
|
86
105
|
|
|
@@ -89,6 +108,9 @@ const UserModel = {
|
|
|
89
108
|
const result = db.prepare(
|
|
90
109
|
"UPDATE users SET password_hash = ?, updated_at = datetime('now') WHERE id = ?"
|
|
91
110
|
).run(passwordHash, id);
|
|
111
|
+
if (result.changes > 0) {
|
|
112
|
+
modelCache.invalidateUser(id);
|
|
113
|
+
}
|
|
92
114
|
return result.changes > 0;
|
|
93
115
|
},
|
|
94
116
|
|
|
@@ -123,6 +145,9 @@ const UserModel = {
|
|
|
123
145
|
params.push(id);
|
|
124
146
|
|
|
125
147
|
const result = db.prepare(`UPDATE users SET ${sets.join(', ')} WHERE id = ?`).run(...params);
|
|
148
|
+
if (result.changes > 0) {
|
|
149
|
+
modelCache.invalidateUser(id);
|
|
150
|
+
}
|
|
126
151
|
return result.changes > 0;
|
|
127
152
|
}
|
|
128
153
|
};
|
package/src/models/version.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const db = require('../database');
|
|
2
|
+
const modelCache = require('../utils/model-cache');
|
|
2
3
|
|
|
3
4
|
const VersionModel = {
|
|
4
5
|
// 创建新版本
|
|
@@ -7,6 +8,7 @@ const VersionModel = {
|
|
|
7
8
|
INSERT INTO skill_versions (skill_id, version, changelog, zip_path, uploader_id, description)
|
|
8
9
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
9
10
|
`).run(skillId, version, changelog || '', zipPath, uploaderId, description || '');
|
|
11
|
+
modelCache.invalidateSkill(skillId);
|
|
10
12
|
return this.findById(result.lastInsertRowid);
|
|
11
13
|
},
|
|
12
14
|
|
|
@@ -22,44 +24,60 @@ const VersionModel = {
|
|
|
22
24
|
|
|
23
25
|
// 根据 skill_id 和 version 查询
|
|
24
26
|
findByVersion(skillId, version) {
|
|
25
|
-
return
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
return modelCache.remember(
|
|
28
|
+
modelCache.keys.skillVersion(skillId, version),
|
|
29
|
+
() => db.prepare(`
|
|
30
|
+
SELECT sv.*, u.username as uploader_username, u.name as uploader_name
|
|
31
|
+
FROM skill_versions sv
|
|
32
|
+
LEFT JOIN users u ON sv.uploader_id = u.id
|
|
33
|
+
WHERE sv.skill_id = ? AND sv.version = ?
|
|
34
|
+
`).get(skillId, version),
|
|
35
|
+
modelCache.refs.version
|
|
36
|
+
);
|
|
31
37
|
},
|
|
32
38
|
|
|
33
39
|
// 列出某 Skill 的所有版本(按创建时间倒序)
|
|
34
40
|
listBySkillId(skillId) {
|
|
35
|
-
return
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
return modelCache.remember(
|
|
42
|
+
modelCache.keys.skillVersions(skillId),
|
|
43
|
+
() => db.prepare(`
|
|
44
|
+
SELECT sv.*, u.username as uploader_username, u.name as uploader_name
|
|
45
|
+
FROM skill_versions sv
|
|
46
|
+
LEFT JOIN users u ON sv.uploader_id = u.id
|
|
47
|
+
WHERE sv.skill_id = ?
|
|
48
|
+
ORDER BY sv.created_at DESC, sv.id DESC
|
|
49
|
+
`).all(skillId),
|
|
50
|
+
(versions) => modelCache.refs.versionList(skillId, versions)
|
|
51
|
+
);
|
|
42
52
|
},
|
|
43
53
|
|
|
44
54
|
// 获取某 Skill 的最新版本
|
|
45
55
|
getLatest(skillId) {
|
|
46
|
-
return
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
return modelCache.remember(
|
|
57
|
+
modelCache.keys.skillLatest(skillId),
|
|
58
|
+
() => db.prepare(`
|
|
59
|
+
SELECT sv.*, u.username as uploader_username, u.name as uploader_name
|
|
60
|
+
FROM skill_versions sv
|
|
61
|
+
LEFT JOIN users u ON sv.uploader_id = u.id
|
|
62
|
+
WHERE sv.skill_id = ?
|
|
63
|
+
ORDER BY sv.created_at DESC, sv.id DESC
|
|
64
|
+
LIMIT 1
|
|
65
|
+
`).get(skillId),
|
|
66
|
+
modelCache.refs.version
|
|
67
|
+
);
|
|
54
68
|
},
|
|
55
69
|
|
|
56
70
|
// 更新版本描述和更新日志
|
|
57
71
|
update(id, description, changelog) {
|
|
72
|
+
const existing = this.findById(id);
|
|
58
73
|
db.prepare(`
|
|
59
74
|
UPDATE skill_versions
|
|
60
75
|
SET description = ?, changelog = ?
|
|
61
76
|
WHERE id = ?
|
|
62
77
|
`).run(description, changelog, id);
|
|
78
|
+
if (existing) {
|
|
79
|
+
modelCache.invalidateSkill(existing.skill_id);
|
|
80
|
+
}
|
|
63
81
|
return this.findById(id);
|
|
64
82
|
}
|
|
65
83
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const db = require('../database');
|
|
2
2
|
const { canManageSkill } = require('../utils/permission');
|
|
3
3
|
const UserModel = require('../models/user');
|
|
4
|
+
const { invalidateSkill } = require('../utils/model-cache');
|
|
4
5
|
|
|
5
6
|
async function collaboratorsRoutes(fastify, options) {
|
|
6
7
|
|
|
@@ -84,6 +85,7 @@ async function collaboratorsRoutes(fastify, options) {
|
|
|
84
85
|
const result = db.prepare(
|
|
85
86
|
'INSERT INTO skill_collaborators (skill_id, user_id, role, created_by) VALUES (?, ?, ?, ?)'
|
|
86
87
|
).run(skill_id, targetUser.id, 'collaborator', request.user.id);
|
|
88
|
+
invalidateSkill(skill_id);
|
|
87
89
|
|
|
88
90
|
return reply.code(201).send({
|
|
89
91
|
ok: true,
|
|
@@ -129,6 +131,7 @@ async function collaboratorsRoutes(fastify, options) {
|
|
|
129
131
|
|
|
130
132
|
db.prepare('DELETE FROM skill_collaborators WHERE skill_id = ? AND user_id = ?')
|
|
131
133
|
.run(skill_id, parseInt(user_id));
|
|
134
|
+
invalidateSkill(skill_id);
|
|
132
135
|
|
|
133
136
|
return reply.send({ ok: true, message: 'Collaborator removed' });
|
|
134
137
|
});
|
|
@@ -192,6 +195,7 @@ async function collaboratorsRoutes(fastify, options) {
|
|
|
192
195
|
});
|
|
193
196
|
|
|
194
197
|
transferTx();
|
|
198
|
+
invalidateSkill(skill_id);
|
|
195
199
|
|
|
196
200
|
return reply.send({
|
|
197
201
|
ok: true,
|
|
@@ -239,6 +243,7 @@ async function collaboratorsRoutes(fastify, options) {
|
|
|
239
243
|
});
|
|
240
244
|
|
|
241
245
|
deleteSkillTx();
|
|
246
|
+
invalidateSkill(skill_id);
|
|
242
247
|
|
|
243
248
|
// 删除文件系统中的文件
|
|
244
249
|
const fs = require('fs');
|
package/src/routes/publish.js
CHANGED
|
@@ -2,6 +2,7 @@ const fs = require('fs');
|
|
|
2
2
|
const db = require('../database');
|
|
3
3
|
const SkillModel = require('../models/skill');
|
|
4
4
|
const VersionModel = require('../models/version');
|
|
5
|
+
const { invalidateSkill } = require('../utils/model-cache');
|
|
5
6
|
const { ensureSkillDir, generateVersionNumber, getZipPath, getZipRelativePath } = require('../utils/zip');
|
|
6
7
|
const { canPublishSkill } = require('../utils/permission');
|
|
7
8
|
|
|
@@ -96,6 +97,7 @@ async function publishRoutes(fastify, options) {
|
|
|
96
97
|
|
|
97
98
|
// 更新 skill 的最新版本
|
|
98
99
|
SkillModel.updateLatestVersion(skill_id, version);
|
|
100
|
+
invalidateSkill(skill_id);
|
|
99
101
|
|
|
100
102
|
return {
|
|
101
103
|
ok: true,
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
class LruCache {
|
|
2
|
+
constructor({ maxBytes = 50 * 1024 * 1024, defaultTtlMs = 0 } = {}) {
|
|
3
|
+
this.maxBytes = Number.isFinite(maxBytes) ? Math.max(0, Math.floor(maxBytes)) : 0;
|
|
4
|
+
this.defaultTtlMs = Number.isFinite(defaultTtlMs) ? Math.max(0, defaultTtlMs) : 0;
|
|
5
|
+
this.map = new Map();
|
|
6
|
+
this.totalBytes = 0;
|
|
7
|
+
this.hits = 0;
|
|
8
|
+
this.misses = 0;
|
|
9
|
+
this.evictions = 0;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static estimateSize(value) {
|
|
13
|
+
try {
|
|
14
|
+
const serialized = JSON.stringify(value);
|
|
15
|
+
return Buffer.byteLength(serialized === undefined ? 'null' : serialized, 'utf8');
|
|
16
|
+
} catch (error) {
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
has(key) {
|
|
22
|
+
const entry = this.map.get(key);
|
|
23
|
+
if (!entry) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (this.#isExpired(entry)) {
|
|
28
|
+
this.#deleteEntry(key, entry);
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get(key) {
|
|
36
|
+
const entry = this.map.get(key);
|
|
37
|
+
if (!entry) {
|
|
38
|
+
this.misses += 1;
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (this.#isExpired(entry)) {
|
|
43
|
+
this.#deleteEntry(key, entry);
|
|
44
|
+
this.misses += 1;
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
this.map.delete(key);
|
|
49
|
+
this.map.set(key, entry);
|
|
50
|
+
this.hits += 1;
|
|
51
|
+
return entry.value;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
set(key, value, options = {}) {
|
|
55
|
+
if (this.maxBytes <= 0) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const size = Number.isFinite(options.size) ? Math.max(0, Math.floor(options.size)) : LruCache.estimateSize(value);
|
|
60
|
+
if (size > this.maxBytes) {
|
|
61
|
+
this.delete(key);
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const ttlMs = Number.isFinite(options.ttlMs) ? Math.max(0, options.ttlMs) : this.defaultTtlMs;
|
|
66
|
+
const expiresAt = ttlMs > 0 ? Date.now() + ttlMs : null;
|
|
67
|
+
const entry = {
|
|
68
|
+
value,
|
|
69
|
+
size,
|
|
70
|
+
refs: new Set(options.refs || []),
|
|
71
|
+
expiresAt
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const existing = this.map.get(key);
|
|
75
|
+
if (existing) {
|
|
76
|
+
this.#deleteEntry(key, existing);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
this.map.set(key, entry);
|
|
80
|
+
this.totalBytes += size;
|
|
81
|
+
this.#evictIfNeeded();
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
delete(key) {
|
|
86
|
+
const entry = this.map.get(key);
|
|
87
|
+
if (!entry) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
this.#deleteEntry(key, entry);
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
clear() {
|
|
96
|
+
this.map.clear();
|
|
97
|
+
this.totalBytes = 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
clearByPrefix(prefix) {
|
|
101
|
+
const keys = [];
|
|
102
|
+
for (const key of this.map.keys()) {
|
|
103
|
+
if (key.startsWith(prefix)) {
|
|
104
|
+
keys.push(key);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
for (const key of keys) {
|
|
109
|
+
this.delete(key);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return keys.length;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
clearByRef(ref) {
|
|
116
|
+
const keys = [];
|
|
117
|
+
for (const [key, entry] of this.map.entries()) {
|
|
118
|
+
if (entry.refs.has(ref)) {
|
|
119
|
+
keys.push(key);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
for (const key of keys) {
|
|
124
|
+
this.delete(key);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return keys.length;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
stats() {
|
|
131
|
+
return {
|
|
132
|
+
entries: this.map.size,
|
|
133
|
+
totalBytes: this.totalBytes,
|
|
134
|
+
maxBytes: this.maxBytes,
|
|
135
|
+
hits: this.hits,
|
|
136
|
+
misses: this.misses,
|
|
137
|
+
evictions: this.evictions
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
#isExpired(entry) {
|
|
142
|
+
return entry.expiresAt !== null && Date.now() > entry.expiresAt;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
#deleteEntry(key, entry) {
|
|
146
|
+
this.map.delete(key);
|
|
147
|
+
this.totalBytes = Math.max(0, this.totalBytes - entry.size);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
#evictIfNeeded() {
|
|
151
|
+
while (this.totalBytes > this.maxBytes && this.map.size > 0) {
|
|
152
|
+
const oldestKey = this.map.keys().next().value;
|
|
153
|
+
const oldestEntry = this.map.get(oldestKey);
|
|
154
|
+
this.#deleteEntry(oldestKey, oldestEntry);
|
|
155
|
+
this.evictions += 1;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
module.exports = LruCache;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
const LruCache = require('./lru-cache');
|
|
2
|
+
|
|
3
|
+
const DEFAULT_CACHE_MAX_MB = 50;
|
|
4
|
+
|
|
5
|
+
function parseCacheMaxBytes() {
|
|
6
|
+
const raw = process.env.CACHE_MAX_MB;
|
|
7
|
+
if (raw === undefined || raw === null || raw === '') {
|
|
8
|
+
return DEFAULT_CACHE_MAX_MB * 1024 * 1024;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const parsed = Number(raw);
|
|
12
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
13
|
+
return DEFAULT_CACHE_MAX_MB * 1024 * 1024;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return Math.floor(parsed * 1024 * 1024);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const cache = new LruCache({ maxBytes: parseCacheMaxBytes() });
|
|
20
|
+
|
|
21
|
+
function uniqueRefs(refs) {
|
|
22
|
+
return Array.from(new Set((refs || []).filter(Boolean)));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function remember(key, loader, refsBuilder, options = {}) {
|
|
26
|
+
const cachedValue = cache.get(key);
|
|
27
|
+
if (cachedValue !== undefined) {
|
|
28
|
+
return cachedValue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const value = loader();
|
|
32
|
+
if (value === undefined) {
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const refs = typeof refsBuilder === 'function' ? refsBuilder(value) : refsBuilder;
|
|
37
|
+
cache.set(key, value, {
|
|
38
|
+
refs: uniqueRefs(refs),
|
|
39
|
+
ttlMs: options.ttlMs
|
|
40
|
+
});
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function skillKey(skillId) {
|
|
45
|
+
return `skill:${skillId}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function skillSearchKey(query) {
|
|
49
|
+
return `skill-search:${query || ''}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function skillExistsKey(skillId) {
|
|
53
|
+
return `skill-exists:${skillId}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function skillVersionsKey(skillId) {
|
|
57
|
+
return `skill-versions:${skillId}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function skillVersionKey(skillId, version) {
|
|
61
|
+
return `skill-version:${skillId}:${version}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function skillLatestKey(skillId) {
|
|
65
|
+
return `skill-latest:${skillId}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function userBasicKey(userId) {
|
|
69
|
+
return `user-basic:${userId}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function skillRefs(skill) {
|
|
73
|
+
if (!skill) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
return [`skill:${skill.id}`, `user:${skill.owner_id}`];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function skillSearchRefs(skills) {
|
|
80
|
+
const refs = ['collection:skill-search'];
|
|
81
|
+
for (const skill of skills || []) {
|
|
82
|
+
refs.push(...skillRefs(skill));
|
|
83
|
+
}
|
|
84
|
+
return refs;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function versionRefs(version) {
|
|
88
|
+
if (!version) {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
return [
|
|
92
|
+
`skill:${version.skill_id}`,
|
|
93
|
+
`version:${version.skill_id}:${version.version}`,
|
|
94
|
+
`user:${version.uploader_id}`
|
|
95
|
+
];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function versionListRefs(skillId, versions) {
|
|
99
|
+
const refs = [`skill:${skillId}`];
|
|
100
|
+
for (const version of versions || []) {
|
|
101
|
+
refs.push(...versionRefs(version));
|
|
102
|
+
}
|
|
103
|
+
return refs;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function userRefs(user) {
|
|
107
|
+
if (!user) {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
return [`user:${user.id}`];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function invalidateSkill(skillId) {
|
|
114
|
+
cache.delete(skillKey(skillId));
|
|
115
|
+
cache.delete(skillExistsKey(skillId));
|
|
116
|
+
cache.delete(skillVersionsKey(skillId));
|
|
117
|
+
cache.delete(skillLatestKey(skillId));
|
|
118
|
+
cache.clearByPrefix(`skill-version:${skillId}:`);
|
|
119
|
+
cache.clearByRef(`skill:${skillId}`);
|
|
120
|
+
cache.clearByRef('collection:skill-search');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function invalidateUser(userId) {
|
|
124
|
+
cache.delete(userBasicKey(userId));
|
|
125
|
+
cache.clearByRef(`user:${userId}`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function invalidateAllSkillSearches() {
|
|
129
|
+
cache.clearByRef('collection:skill-search');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function getStats() {
|
|
133
|
+
return cache.stats();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
module.exports = {
|
|
137
|
+
remember,
|
|
138
|
+
getStats,
|
|
139
|
+
invalidateSkill,
|
|
140
|
+
invalidateUser,
|
|
141
|
+
invalidateAllSkillSearches,
|
|
142
|
+
keys: {
|
|
143
|
+
skill: skillKey,
|
|
144
|
+
skillSearch: skillSearchKey,
|
|
145
|
+
skillExists: skillExistsKey,
|
|
146
|
+
skillVersions: skillVersionsKey,
|
|
147
|
+
skillVersion: skillVersionKey,
|
|
148
|
+
skillLatest: skillLatestKey,
|
|
149
|
+
userBasic: userBasicKey
|
|
150
|
+
},
|
|
151
|
+
refs: {
|
|
152
|
+
skill: skillRefs,
|
|
153
|
+
skillSearch: skillSearchRefs,
|
|
154
|
+
version: versionRefs,
|
|
155
|
+
versionList: versionListRefs,
|
|
156
|
+
user: userRefs
|
|
157
|
+
}
|
|
158
|
+
};
|