• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Seniru / defendxstore / 14091969004

26 Mar 2025 07:35PM UTC coverage: 64.797% (-6.3%) from 71.123%
14091969004

push

github

web-flow
feat: user interfaces and backend (partial) (#46)

* feat: add inventory management (#37)

* feat: add inventory management

* feat: updated inventory management with relevant changes

* Prettier applied

* chore: restore package json configurations

* update: minor styling changes

* feat: supply and sales management (#26)

* feat: create supply management

* feat:SalesManagement Tables,buttons design

* update: prettify

* feat:create revenue,profit,cost charts

---------

Co-authored-by: Seniru Pasan Indira <senirupasan@gmail.com>

* feat: home page (#43)

* feat: add inventory management

* feat: updated inventory management with relevant changes

* Prettier applied

* feat: item component created.Home page completed.

* test

* updated : rename images

---------

Co-authored-by: Seniru Pasan Indira <senirupasan@gmail.com>

* fix: minor fixes

* feat: product page (#45)

* feat: add inventory management

* feat: updated inventory management with relevant changes

* Prettier applied

* feat: item component created.Home page completed.

* test

* updated : rename images

* feat: add checkout page with product details and styling

* fix: updated

* feat: product page done

* feat: refactor product page done the chnages

* refactor: clean up Product page styles and update size guide link

* style: update ProductCard styles and add price formatting

* style: rename product image class to fix

* pretiffy applied

* style: edited css conflitcs

---------

Co-authored-by: Seniru Pasan Indira <senirupasan@gmail.com>

* fix: display profile image

* feat: integrate profile with api

* update: integrate user management with api

* test: add tests for user crud

* feat: create review (#48)

* feat: review

* update:review

---------

Co-authored-by: Seniru Pasan Indira <senirupasan@gmail.com>

* feat: create ticketing ui (#50)

* feat: create forum page

* feat: create forum thread page

* feat: create ticket system interface

---------
Closes #13
Closes #14
... (continued)

91 of 151 branches covered (60.26%)

Branch coverage included in aggregate %.

137 of 301 new or added lines in 20 files covered. (45.51%)

1 existing line in 1 file now uncovered.

371 of 562 relevant lines covered (66.01%)

16.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.87
/backend/src/controllers/users.js
1
const mongoose = require("mongoose")
3✔
2
const bcrypt = require("bcrypt")
3✔
3
const { StatusCodes } = require("http-status-codes")
3✔
4

5
const createResponse = require("../utils/createResponse")
3✔
6
const createToken = require("../utils/createToken")
3✔
7
const User = require("../models/User")
3✔
8
const logger = require("../utils/logger")
3✔
9

10
const permissions = {
3✔
11
    USER: 1 << 0,
12
    DELIVERY_AGENT: 1 << 1,
13
    SUPPORT_AGENT: 1 << 2,
14
    ADMIN: 1 << 3,
15
}
16

17
const getAllUsers = async (req, res, next) => {
3✔
18
    try {
33✔
19
        const search = req.query.search || ""
33✔
20
        const type = req.query.type
33✔
21

22
        if (type && !["USER", "SUPPORT_AGENT", "DELIVERY_AGENT", "ADMIN"].includes(type))
33✔
23
            return createResponse(res, StatusCodes.BAD_REQUEST, "Invalid type")
3✔
24

25
        let users = await User.find(
30✔
26
            { username: { $regex: search, $options: "i" } },
27
            {
28
                username: 1,
29
                email: 1,
30
                deliveryAddress: 1,
31
                contactNumber: 1,
32
                role: 1,
33
                profileImage: {
34
                    $cond: {
35
                        if: { $ifNull: ["$profileImage", false] },
36
                        then: {
37
                            $concat: [
38
                                `${req.protocol}://${req.get("host")}/api/users/`,
39
                                "$username",
40
                                "/profileImage",
41
                            ],
42
                        },
43
                        else: null,
44
                    },
45
                },
46
            },
47
        ).exec()
48

49
        users = users.map((user) => user.applyDerivations())
294✔
50
        // apply filters
51
        if (type) users = users.filter((user) => user.role.includes(type))
225✔
52

53
        return createResponse(res, StatusCodes.OK, { users })
30✔
54
    } catch (error) {
55
        next(error)
×
56
    }
57
}
58

59
const createUser = async (req, res, next) => {
3✔
60
    try {
27✔
61
        const { username, email, password, deliveryAddress, contactNumber, profileImage } = req.body
27✔
62
        if (!password)
27✔
63
            return createResponse(res, StatusCodes.BAD_REQUEST, [
3✔
64
                {
65
                    field: "password",
66
                    message: "You must provide a password",
67
                },
68
            ])
69
        // check if profileImage is in the correct format
70
        if (profileImage && !profileImage.match(/^data:(.+);base64,(.*)$/))
24✔
71
            return createResponse(res, StatusCodes.BAD_REQUEST, [
3✔
72
                {
73
                    field: "profileImage",
74
                    message: "Invalid profile image format",
75
                },
76
            ])
77

78
        const salt = await bcrypt.genSalt(10)
21✔
79
        const hashedPassword = await bcrypt.hash(password, salt)
21✔
80
        const user = new User({
21✔
81
            username,
82
            email,
83
            password: hashedPassword,
84
            deliveryAddress,
85
            contactNumber,
86
            profileImage,
87
        })
88
        await user.save()
21✔
89
        const token = createToken(user)
6✔
90
        return createResponse(res, StatusCodes.CREATED, { token })
6✔
91
    } catch (error) {
92
        if (error instanceof mongoose.Error.ValidationError) {
15✔
93
            return createResponse(
9✔
94
                res,
95
                StatusCodes.BAD_REQUEST,
96
                Object.keys(error.errors).map((key) => ({
9✔
97
                    field: key,
98
                    message: error.errors[key].message,
99
                })),
100
            )
101
        } else if (error.message == "User already exist with this email") {
6✔
102
            return createResponse(res, StatusCodes.CONFLICT, [
3✔
103
                {
104
                    field: "email",
105
                    message: error.message,
106
                },
107
            ])
108
        } else if (error.message == "Username taken") {
3!
109
            return createResponse(res, StatusCodes.CONFLICT, [
3✔
110
                {
111
                    field: "username",
112
                    message: error.message,
113
                },
114
            ])
115
        }
116
        next(error)
×
117
    }
118
}
119

120
const deleteUser = async (req, res, next) => {
3✔
121
    try {
12✔
122
        const { username } = req.params
12✔
123
        if (!req.user.roles.includes("ADMIN") && username !== req.user.username)
12✔
124
            return createResponse(res, StatusCodes.FORBIDDEN, "You cannot delete this user")
3✔
125

126
        const user = await User.findOneAndDelete({ username }).exec()
9✔
127
        if (!user) return createResponse(res, StatusCodes.NOT_FOUND, "User not found")
9✔
128
        return createResponse(res, StatusCodes.OK, "User deleted")
6✔
129
    } catch (error) {
130
        next(error)
×
131
    }
132
}
133

134
const getUser = async (req, res, next) => {
3✔
135
    try {
15✔
136
        const { username } = req.params
15✔
137
        let user = await User.findOne({ username }, "-password").exec()
15✔
138
        if (!user) return createResponse(res, StatusCodes.NOT_FOUND, "User not found")
15✔
139
        user = user.applyDerivations()
12✔
140
        if (
12✔
141
            !req.user.roles.includes("ADMIN") &&
27✔
142
            req.user.username !== username &&
143
            !user.role.includes("DELIVERY_AGENT")
144
        )
145
            return createResponse(
3✔
146
                res,
147
                StatusCodes.FORBIDDEN,
148
                "You are not authorized to view this user",
149
            )
150

151
        return createResponse(res, StatusCodes.OK, { user })
9✔
152
    } catch (error) {
153
        next(error)
×
154
    }
155
}
156

157
const changePassword = async (req, res, next) => {
3✔
158
    try {
12✔
159
        const { username } = req.params
12✔
160
        const { password } = req.body
12✔
161
        if (!req.user.roles.includes("ADMIN") && username !== req.user.username)
12✔
162
            return createResponse(res, StatusCodes.FORBIDDEN, "You cannot edit this user")
3✔
163

164
        if (!password || password.toString() === "")
9!
165
            return createResponse(res, StatusCodes.BAD_REQUEST, "You must provide the password")
×
166

167
        const salt = await bcrypt.genSalt(10)
9✔
168
        const hashedPassword = await bcrypt.hash(password, salt)
9✔
169

170
        const user = await User.findOneAndUpdate({ username }, { password: hashedPassword }).exec()
9✔
171
        if (!user) return createResponse(res, StatusCodes.NOT_FOUND, "User not found")
9✔
172

173
        return createResponse(res, StatusCodes.OK, "Password changed")
6✔
174
    } catch (error) {
175
        next(error)
×
176
    }
177
}
178

179
const getUserProfileImage = async (req, res, next) => {
3✔
180
    try {
9✔
181
        const { username } = req.params
9✔
182
        const user = await User.findOne({ username })
9✔
183
        if (!user) return createResponse(res, StatusCodes.NOT_FOUND, "User not found")
9✔
184
        // return image
185
        const { profileImage } = user
6✔
186
        if (!profileImage)
6✔
187
            return createResponse(res, StatusCodes.NOT_FOUND, "No profile image found")
3✔
188

189
        const match = profileImage.match(/^data:(.+);base64,(.*)$/)
3✔
190

191
        const fileType = match[1]
3✔
192
        const imageData = match[2]
3✔
193

194
        res.status(StatusCodes.OK)
3✔
195
            .set({ "Content-Type": fileType })
196
            .send(Buffer.from(imageData, "base64"))
197
    } catch (error) {
198
        next(error)
×
199
    }
200
}
201

202
const changeProfileImage = async (req, res, next) => {
3✔
203
    try {
21✔
204
        const { username } = req.params
21✔
205
        const { image } = req.body
21✔
206

207
        if (!req.user.roles.includes("ADMIN") && username !== req.user.username)
21✔
208
            return createResponse(res, StatusCodes.FORBIDDEN, "You cannot edit this user")
3✔
209

210
        if (!image || !image.match(/^data:(.+);base64,(.*)$/))
18✔
211
            return createResponse(res, StatusCodes.BAD_REQUEST, "Invalid profile image format")
6✔
212

213
        const user = await User.findOneAndUpdate(
12✔
214
            { username },
215
            { profileImage: image },
216
            { runValidators: true },
217
        ).exec()
218
        if (!user) return createResponse(res, StatusCodes.NOT_FOUND, "User not found")
9✔
219
        return createResponse(res, StatusCodes.OK, "Image updated successfully")
6✔
220
    } catch (error) {
221
        if (error instanceof mongoose.Error.ValidationError) {
3!
222
            return createResponse(
3✔
223
                res,
224
                StatusCodes.BAD_REQUEST,
225
                Object.keys(error.errors).map((key) => ({
3✔
226
                    field: key,
227
                    message: error.errors[key].message,
228
                })),
229
            )
230
        }
UNCOV
231
        next(error)
×
232
    }
233
}
234

235
const addRole = async (req, res, next) => {
3✔
236
    try {
21✔
237
        const { username } = req.params
21✔
238
        const { role } = req.body
21✔
239
        if (!role || !["USER", "DELIVERY_AGENT", "SUPPORT_AGENT", "ADMIN"].includes(role))
21✔
240
            return createResponse(res, StatusCodes.BAD_REQUEST, "Invalid role")
3✔
241

242
        const user = await User.findOne({ username })
18✔
243
        if (!user) return createResponse(res, StatusCodes.NOT_FOUND, "User not found")
18✔
244

245
        user.role |= permissions[role]
15✔
246
        await user.save()
15✔
247
        return createResponse(res, StatusCodes.OK, "Role added")
15✔
248
    } catch (error) {
249
        next(error)
×
250
    }
251
}
252

253
const removeRole = async (req, res, next) => {
3✔
254
    try {
18✔
255
        const { username, role } = req.params
18✔
256
        if (!["USER", "DELIVERY_AGENT", "SUPPORT_AGENT", "ADMIN"].includes(role))
18✔
257
            return createResponse(res, StatusCodes.BAD_REQUEST, "Invalid role")
3✔
258

259
        const user = await User.findOne({ username }).exec()
15✔
260
        if (!user) return createResponse(res, StatusCodes.NOT_FOUND, "User not found")
15✔
261

262
        user.role &= (2 ** Object.keys(permissions).length - 1) & ~permissions[role]
12✔
263
        await user.save()
12✔
264
        return createResponse(res, StatusCodes.OK, "Role removed")
12✔
265
    } catch (error) {
266
        next(error)
×
267
    }
268
}
269

270
const editUser = async (req, res, next) => {
3✔
271
    try {
9✔
272
        const { username } = req.params
9✔
273
        const { deliveryAddress, contactNumber } = req.body
9✔
274

275
        if (!req.user.roles.includes("ADMIN") && username !== req.user.username)
9✔
276
            return createResponse(res, StatusCodes.FORBIDDEN, "You cannot edit this user")
3✔
277

278
        let update = { deliveryAddress }
6✔
279
        if (contactNumber) update.contactNumber = [contactNumber]
6!
280

281
        const user = await User.findOneAndUpdate({ username }, update)
6✔
282

283
        if (!user) return createResponse(res, StatusCodes.NOT_FOUND, "User not found")
6✔
284
        return createResponse(res, StatusCodes.OK, "Editted")
3✔
285
    } catch (error) {
286
        if (error instanceof mongoose.Error.ValidationError) {
×
287
            return createResponse(
×
288
                res,
289
                StatusCodes.BAD_REQUEST,
290
                Object.keys(error.errors).map((key) => ({
×
291
                    field: key,
292
                    message: error.errors[key].message,
293
                })),
294
            )
295
        }
296
        next(error)
×
297
    }
298
}
299

300
module.exports = {
3✔
301
    getAllUsers,
302
    createUser,
303
    deleteUser,
304
    getUser,
305
    changePassword,
306
    getUserProfileImage,
307
    changeProfileImage,
308
    addRole,
309
    removeRole,
310
    editUser,
311
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc