@abtnode/core 1.17.8-beta-20260119-102944-6ba93a16 → 1.17.8-beta-20260125-093329-64b43854

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.
@@ -2,6 +2,8 @@ const logger = require('@abtnode/logger')('@abtnode/core:api:team:org-crud');
2
2
  const { CustomError } = require('@blocklet/error');
3
3
  const md5 = require('@abtnode/util/lib/md5');
4
4
  const { sanitizeTag } = require('@abtnode/util/lib/sanitize');
5
+ const path = require('path');
6
+ const fs = require('fs-extra');
5
7
 
6
8
  const { createOrgValidators } = require('../../util/org');
7
9
  const { createOrgInputSchema, updateOrgInputSchema } = require('../../validators/org');
@@ -159,7 +161,33 @@ async function updateOrg(api, { teamDid, org }, context) {
159
161
  }
160
162
 
161
163
  const state = await api.getOrgState(teamDid);
162
- return state.updateOrg({ org: sanitizedOrg }, context);
164
+
165
+ // 获取旧的 org 数据,用于比对 avatar 是否发生变化
166
+ const oldOrg = await state.getOrg(org.id);
167
+
168
+ const result = await state.updateOrg({ org: sanitizedOrg }, context);
169
+
170
+ // 如果 avatar 发生变化,删除旧的 avatar 文件(不影响主流程)
171
+ try {
172
+ if (oldOrg?.avatar && result.avatar && oldOrg.avatar !== result.avatar) {
173
+ const blocklet = await getBlocklet({
174
+ did: teamDid,
175
+ states: api.states,
176
+ dataDirs: api.dataDirs,
177
+ useCache: true,
178
+ });
179
+
180
+ const oldAvatarPath = path.resolve(blocklet.env.dataDir, 'orgs', oldOrg.avatar);
181
+ if (fs.existsSync(oldAvatarPath)) {
182
+ await fs.remove(oldAvatarPath);
183
+ logger.info('Successfully removed old org avatar', { teamDid, orgId: result.id, oldAvatar: oldOrg.avatar });
184
+ }
185
+ }
186
+ } catch (err) {
187
+ logger.error('remove org avatar file', { err, teamDid, orgId: result.id, avatar: result.avatar });
188
+ }
189
+
190
+ return result;
163
191
  } catch (err) {
164
192
  logger.error('Failed to update org', { err, teamDid });
165
193
  throw err;
@@ -99,6 +99,7 @@ async function getUsers(api, { teamDid, query, paging: inputPaging, sort, dids }
99
99
 
100
100
  'metadata',
101
101
  'isFollowing',
102
+ 'name',
102
103
  ]);
103
104
 
104
105
  return {
@@ -65,7 +65,11 @@ async function getBlockletBlurhash(manager, { did }) {
65
65
  * @param {Object} context
66
66
  * @returns {Promise<Object>}
67
67
  */
68
- async function updateBlockletSettings(manager, { did, enableSessionHardening, invite, gateway, aigne, org }, context) {
68
+ async function updateBlockletSettings(
69
+ manager,
70
+ { did, enableSessionHardening, invite, gateway, aigne, org, subService },
71
+ context
72
+ ) {
69
73
  const params = {};
70
74
  if (!isNil(enableSessionHardening)) {
71
75
  params.enableSessionHardening = enableSessionHardening;
@@ -98,6 +102,37 @@ async function updateBlockletSettings(manager, { did, enableSessionHardening, in
98
102
  };
99
103
  }
100
104
 
105
+ if (!isNil(subService)) {
106
+ // Security: Validate subService configuration to prevent path traversal
107
+ const SUB_SERVICE_SCHEMA = Joi.object({
108
+ enabled: Joi.boolean().required(),
109
+ domain: Joi.when('enabled', {
110
+ is: true,
111
+ then: Joi.string().required(),
112
+ otherwise: Joi.string().optional().allow(''),
113
+ }),
114
+ staticRoot: Joi.when('enabled', {
115
+ is: true,
116
+ then: Joi.string()
117
+ .required()
118
+ // Reject paths containing ".." or starting with "/"
119
+ .pattern(/^[^/]/, { name: 'no-absolute-path' })
120
+ .pattern(/^(?!.*\.\.).*$/, { name: 'no-parent-reference' })
121
+ .messages({
122
+ 'string.pattern.name': 'Invalid path. Path cannot contain ".." or start with "/"',
123
+ }),
124
+ otherwise: Joi.string().optional().allow(''),
125
+ }),
126
+ });
127
+
128
+ const { error } = SUB_SERVICE_SCHEMA.validate(subService);
129
+ if (error) {
130
+ throw new CustomError(400, error.message);
131
+ }
132
+
133
+ params.subService = subService;
134
+ }
135
+
101
136
  let shouldRotateSession = false;
102
137
  if (!isNil(org)) {
103
138
  const ORG_SCHEMA = Joi.object({
@@ -826,10 +826,10 @@ class DiskBlockletManager extends BaseBlockletManager {
826
826
  return summary;
827
827
  }
828
828
 
829
- updateBlockletSettings({ did, enableSessionHardening, invite, gateway, aigne, org }, context) {
829
+ updateBlockletSettings({ did, enableSessionHardening, invite, gateway, aigne, org, subService }, context) {
830
830
  return settingsManager.updateBlockletSettings(
831
831
  this,
832
- { did, enableSessionHardening, invite, gateway, aigne, org },
832
+ { did, enableSessionHardening, invite, gateway, aigne, org, subService },
833
833
  context
834
834
  );
835
835
  }