Skip to content

Commit 8bc0906

Browse files
committed
feat: add environment variable labels
1 parent 7a8917f commit 8bc0906

10 files changed

Lines changed: 792 additions & 9 deletions

File tree

back/api/env.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ const storage = multer.diskStorage({
1818
},
1919
});
2020
const upload = multer({ storage: storage });
21+
const labelSchema = Joi.array()
22+
.items(Joi.string().trim().required())
23+
.min(1)
24+
.required();
2125

2226
export default (app: Router) => {
2327
app.use('/envs', route);
@@ -44,6 +48,7 @@ export default (app: Router) => {
4448
.required()
4549
.pattern(/^[a-zA-Z_][0-9a-zA-Z_]*$/),
4650
remarks: Joi.string().optional().allow(''),
51+
labels: Joi.array().items(Joi.string().trim()).optional(),
4752
}),
4853
),
4954
}),
@@ -70,6 +75,7 @@ export default (app: Router) => {
7075
name: Joi.string().required(),
7176
remarks: Joi.string().optional().allow('').allow(null),
7277
id: Joi.number().required(),
78+
labels: Joi.array().items(Joi.string().trim()).optional(),
7379
}),
7480
}),
7581
async (req: Request, res: Response, next: NextFunction) => {
@@ -230,6 +236,44 @@ export default (app: Router) => {
230236
},
231237
);
232238

239+
route.post(
240+
'/labels',
241+
celebrate({
242+
body: Joi.object({
243+
ids: Joi.array().items(Joi.number().required()).min(1).required(),
244+
labels: labelSchema,
245+
}),
246+
}),
247+
async (req: Request, res: Response, next: NextFunction) => {
248+
try {
249+
const envService = Container.get(EnvService);
250+
const data = await envService.addLabels(req.body.ids, req.body.labels);
251+
return res.send({ code: 200, data });
252+
} catch (e) {
253+
return next(e);
254+
}
255+
},
256+
);
257+
258+
route.delete(
259+
'/labels',
260+
celebrate({
261+
body: Joi.object({
262+
ids: Joi.array().items(Joi.number().required()).min(1).required(),
263+
labels: labelSchema,
264+
}),
265+
}),
266+
async (req: Request, res: Response, next: NextFunction) => {
267+
try {
268+
const envService = Container.get(EnvService);
269+
const data = await envService.removeLabels(req.body.ids, req.body.labels);
270+
return res.send({ code: 200, data });
271+
} catch (e) {
272+
return next(e);
273+
}
274+
},
275+
);
276+
233277
route.post(
234278
'/upload',
235279
upload.single('env'),
@@ -248,6 +292,7 @@ export default (app: Router) => {
248292
name: x.name,
249293
value: x.value,
250294
remarks: x.remarks,
295+
labels: x.labels,
251296
})),
252297
);
253298
return res.send({ code: 200, data: result });

back/data/env.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export class Env {
1010
name?: string;
1111
remarks?: string;
1212
isPinned?: 1 | 0;
13+
labels?: string[];
1314

1415
constructor(options: Env) {
1516
this.value = options.value;
@@ -23,6 +24,7 @@ export class Env {
2324
this.name = options.name;
2425
this.remarks = options.remarks || '';
2526
this.isPinned = options.isPinned || 0;
27+
this.labels = options.labels || [];
2628
}
2729
}
2830

@@ -45,4 +47,5 @@ export const EnvModel = sequelize.define<EnvInstance>('Env', {
4547
name: { type: DataTypes.STRING, unique: 'compositeIndex' },
4648
remarks: DataTypes.STRING,
4749
isPinned: DataTypes.NUMBER,
50+
labels: DataTypes.JSON,
4851
});

back/loaders/db.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export default async () => {
4040
type: 'NUMBER',
4141
},
4242
{ table: 'Envs', column: 'isPinned', type: 'NUMBER' },
43+
{ table: 'Envs', column: 'labels', type: 'JSON' },
4344
];
4445

4546
for (const migration of migrations) {

back/services/env.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,44 @@ export default class EnvService {
199199
await EnvModel.update({ isPinned: 0 }, { where: { id: ids } });
200200
}
201201

202+
public async addLabels(ids: number[], labels: string[]) {
203+
await sequelize.transaction(async (transaction) => {
204+
const docs = await EnvModel.findAll({
205+
where: { id: ids },
206+
transaction,
207+
});
208+
for (const doc of docs) {
209+
const env = doc.get({ plain: true });
210+
await EnvModel.update(
211+
{ labels: Array.from(new Set([...(env.labels || []), ...labels])) },
212+
{ where: { id: env.id }, transaction },
213+
);
214+
}
215+
});
216+
return await this.find({ id: ids });
217+
}
218+
219+
public async removeLabels(ids: number[], labels: string[]) {
220+
await sequelize.transaction(async (transaction) => {
221+
const docs = await EnvModel.findAll({
222+
where: { id: ids },
223+
transaction,
224+
});
225+
for (const doc of docs) {
226+
const env = doc.get({ plain: true });
227+
await EnvModel.update(
228+
{
229+
labels: (env.labels || []).filter(
230+
(label: string) => !labels.includes(label),
231+
),
232+
},
233+
{ where: { id: env.id }, transaction },
234+
);
235+
}
236+
});
237+
return await this.find({ id: ids });
238+
}
239+
202240
public async set_envs() {
203241
const envs = await this.envs('', {
204242
name: { [Op.not]: null },

0 commit comments

Comments
 (0)