@intranefr/superbackend 1.4.3
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/.commiat +4 -0
- package/.env.example +47 -0
- package/README.md +110 -0
- package/index.js +94 -0
- package/package.json +67 -0
- package/public/css/styles.css +139 -0
- package/public/js/animations.js +41 -0
- package/sdk/error-tracking/browser/package.json +16 -0
- package/sdk/error-tracking/browser/src/core.js +270 -0
- package/sdk/error-tracking/browser/src/embed.js +18 -0
- package/sdk/error-tracking/browser/src/index.js +1 -0
- package/server.js +5 -0
- package/src/admin/endpointRegistry.js +300 -0
- package/src/controllers/admin.controller.js +321 -0
- package/src/controllers/adminAssets.controller.js +530 -0
- package/src/controllers/adminAssetsStorage.controller.js +260 -0
- package/src/controllers/adminEjsVirtual.controller.js +354 -0
- package/src/controllers/adminFeatureFlags.controller.js +155 -0
- package/src/controllers/adminHeadless.controller.js +1071 -0
- package/src/controllers/adminI18n.controller.js +604 -0
- package/src/controllers/adminJsonConfigs.controller.js +97 -0
- package/src/controllers/adminLlm.controller.js +273 -0
- package/src/controllers/adminMigration.controller.js +257 -0
- package/src/controllers/adminSeoConfig.controller.js +515 -0
- package/src/controllers/adminStats.controller.js +121 -0
- package/src/controllers/adminUploadNamespaces.controller.js +208 -0
- package/src/controllers/assets.controller.js +248 -0
- package/src/controllers/auth.controller.js +93 -0
- package/src/controllers/billing.controller.js +223 -0
- package/src/controllers/featureFlags.controller.js +35 -0
- package/src/controllers/forms.controller.js +217 -0
- package/src/controllers/globalSettings.controller.js +252 -0
- package/src/controllers/headlessCrud.controller.js +126 -0
- package/src/controllers/i18n.controller.js +12 -0
- package/src/controllers/invite.controller.js +249 -0
- package/src/controllers/jsonConfigs.controller.js +19 -0
- package/src/controllers/metrics.controller.js +149 -0
- package/src/controllers/notificationAdmin.controller.js +264 -0
- package/src/controllers/notifications.controller.js +131 -0
- package/src/controllers/org.controller.js +357 -0
- package/src/controllers/orgAdmin.controller.js +491 -0
- package/src/controllers/stripeAdmin.controller.js +410 -0
- package/src/controllers/user.controller.js +361 -0
- package/src/controllers/userAdmin.controller.js +277 -0
- package/src/controllers/waitingList.controller.js +167 -0
- package/src/controllers/webhook.controller.js +200 -0
- package/src/middleware/auth.js +66 -0
- package/src/middleware/errorCapture.js +170 -0
- package/src/middleware/headlessApiTokenAuth.js +57 -0
- package/src/middleware/org.js +108 -0
- package/src/middleware.js +901 -0
- package/src/models/ActionEvent.js +31 -0
- package/src/models/ActivityLog.js +41 -0
- package/src/models/Asset.js +84 -0
- package/src/models/AuditEvent.js +93 -0
- package/src/models/EmailLog.js +28 -0
- package/src/models/ErrorAggregate.js +72 -0
- package/src/models/FormSubmission.js +41 -0
- package/src/models/GlobalSetting.js +38 -0
- package/src/models/HeadlessApiToken.js +24 -0
- package/src/models/HeadlessModelDefinition.js +41 -0
- package/src/models/I18nEntry.js +77 -0
- package/src/models/I18nLocale.js +33 -0
- package/src/models/Invite.js +70 -0
- package/src/models/JsonConfig.js +46 -0
- package/src/models/Notification.js +60 -0
- package/src/models/Organization.js +57 -0
- package/src/models/OrganizationMember.js +43 -0
- package/src/models/StripeCatalogItem.js +77 -0
- package/src/models/StripeWebhookEvent.js +57 -0
- package/src/models/User.js +89 -0
- package/src/models/VirtualEjsFile.js +60 -0
- package/src/models/VirtualEjsFileVersion.js +43 -0
- package/src/models/VirtualEjsGroupChange.js +32 -0
- package/src/models/WaitingList.js +41 -0
- package/src/models/Webhook.js +63 -0
- package/src/models/Workflow.js +29 -0
- package/src/models/WorkflowExecution.js +12 -0
- package/src/routes/admin.routes.js +26 -0
- package/src/routes/adminAssets.routes.js +28 -0
- package/src/routes/adminAssetsStorage.routes.js +13 -0
- package/src/routes/adminAudit.routes.js +196 -0
- package/src/routes/adminEjsVirtual.routes.js +17 -0
- package/src/routes/adminErrors.routes.js +164 -0
- package/src/routes/adminFeatureFlags.routes.js +12 -0
- package/src/routes/adminHeadless.routes.js +38 -0
- package/src/routes/adminI18n.routes.js +22 -0
- package/src/routes/adminJsonConfigs.routes.js +15 -0
- package/src/routes/adminLlm.routes.js +12 -0
- package/src/routes/adminMigration.routes.js +81 -0
- package/src/routes/adminSeoConfig.routes.js +20 -0
- package/src/routes/adminUploadNamespaces.routes.js +13 -0
- package/src/routes/assets.routes.js +21 -0
- package/src/routes/auth.routes.js +12 -0
- package/src/routes/billing.routes.js +11 -0
- package/src/routes/errorTracking.routes.js +31 -0
- package/src/routes/featureFlags.routes.js +9 -0
- package/src/routes/forms.routes.js +9 -0
- package/src/routes/formsAdmin.routes.js +13 -0
- package/src/routes/globalSettings.routes.js +18 -0
- package/src/routes/headless.routes.js +15 -0
- package/src/routes/i18n.routes.js +8 -0
- package/src/routes/invite.routes.js +9 -0
- package/src/routes/jsonConfigs.routes.js +8 -0
- package/src/routes/log.routes.js +111 -0
- package/src/routes/metrics.routes.js +9 -0
- package/src/routes/notificationAdmin.routes.js +15 -0
- package/src/routes/notifications.routes.js +12 -0
- package/src/routes/org.routes.js +31 -0
- package/src/routes/orgAdmin.routes.js +20 -0
- package/src/routes/publicAssets.routes.js +7 -0
- package/src/routes/stripeAdmin.routes.js +20 -0
- package/src/routes/user.routes.js +22 -0
- package/src/routes/userAdmin.routes.js +15 -0
- package/src/routes/waitingList.routes.js +13 -0
- package/src/routes/waitingListAdmin.routes.js +9 -0
- package/src/routes/webhook.routes.js +32 -0
- package/src/routes/workflowWebhook.routes.js +54 -0
- package/src/routes/workflows.routes.js +110 -0
- package/src/services/assets.service.js +110 -0
- package/src/services/audit.service.js +62 -0
- package/src/services/auditLogger.js +165 -0
- package/src/services/ejsVirtual.service.js +614 -0
- package/src/services/email.service.js +351 -0
- package/src/services/errorLogger.js +221 -0
- package/src/services/featureFlags.service.js +202 -0
- package/src/services/forms.service.js +214 -0
- package/src/services/globalSettings.service.js +49 -0
- package/src/services/headlessApiTokens.service.js +158 -0
- package/src/services/headlessCrypto.service.js +31 -0
- package/src/services/headlessModels.service.js +356 -0
- package/src/services/i18n.service.js +314 -0
- package/src/services/i18nInferredKeys.service.js +337 -0
- package/src/services/jsonConfigs.service.js +392 -0
- package/src/services/llm.service.js +749 -0
- package/src/services/migration.service.js +581 -0
- package/src/services/migrationAssets/fsLocal.js +58 -0
- package/src/services/migrationAssets/index.js +134 -0
- package/src/services/migrationAssets/s3.js +75 -0
- package/src/services/migrationAssets/sftp.js +92 -0
- package/src/services/notification.service.js +212 -0
- package/src/services/objectStorage.service.js +514 -0
- package/src/services/seoConfig.service.js +402 -0
- package/src/services/storage.js +150 -0
- package/src/services/stripe.service.js +185 -0
- package/src/services/stripeHelper.service.js +264 -0
- package/src/services/uploadNamespaces.service.js +326 -0
- package/src/services/webhook.service.js +157 -0
- package/src/services/workflow.service.js +271 -0
- package/src/utils/asyncHandler.js +5 -0
- package/src/utils/encryption.js +80 -0
- package/src/utils/jwt.js +40 -0
- package/src/utils/orgRoles.js +156 -0
- package/src/utils/validation.js +26 -0
- package/src/utils/webhookRetry.js +93 -0
- package/views/admin-assets.ejs +444 -0
- package/views/admin-audit.ejs +283 -0
- package/views/admin-coolify-deploy.ejs +207 -0
- package/views/admin-dashboard-home.ejs +291 -0
- package/views/admin-dashboard.ejs +397 -0
- package/views/admin-ejs-virtual.ejs +280 -0
- package/views/admin-errors.ejs +368 -0
- package/views/admin-feature-flags.ejs +390 -0
- package/views/admin-forms.ejs +526 -0
- package/views/admin-global-settings.ejs +436 -0
- package/views/admin-headless.ejs +2020 -0
- package/views/admin-i18n-locales.ejs +221 -0
- package/views/admin-i18n.ejs +728 -0
- package/views/admin-json-configs.ejs +410 -0
- package/views/admin-llm.ejs +884 -0
- package/views/admin-metrics.ejs +274 -0
- package/views/admin-migration.ejs +814 -0
- package/views/admin-notifications.ejs +430 -0
- package/views/admin-organizations.ejs +984 -0
- package/views/admin-seo-config.ejs +673 -0
- package/views/admin-stripe-pricing.ejs +558 -0
- package/views/admin-test.ejs +342 -0
- package/views/admin-users.ejs +452 -0
- package/views/admin-waiting-list.ejs +547 -0
- package/views/admin-webhooks.ejs +329 -0
- package/views/admin-workflows.ejs +310 -0
- package/views/partials/admin-assets-script.ejs +2022 -0
- package/views/partials/admin-test-sidebar.ejs +14 -0
- package/views/partials/dashboard/nav-items.ejs +66 -0
- package/views/partials/dashboard/palette.ejs +63 -0
- package/views/partials/dashboard/sidebar.ejs +21 -0
- package/views/partials/dashboard/tab-bar.ejs +26 -0
- package/views/partials/footer.ejs +3 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
const Organization = require('../models/Organization');
|
|
2
|
+
const OrganizationMember = require('../models/OrganizationMember');
|
|
3
|
+
const User = require('../models/User');
|
|
4
|
+
const emailService = require('../services/email.service');
|
|
5
|
+
const { isValidOrgRole, getAllowedOrgRoles, getDefaultOrgRole } = require('../utils/orgRoles');
|
|
6
|
+
|
|
7
|
+
const generateSlug = (name) => {
|
|
8
|
+
return name
|
|
9
|
+
.toLowerCase()
|
|
10
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
11
|
+
.replace(/^-|-$/g, '')
|
|
12
|
+
.substring(0, 50) + '-' + Date.now().toString(36);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
exports.listOrgs = async (req, res) => {
|
|
16
|
+
try {
|
|
17
|
+
const memberships = await OrganizationMember.find({
|
|
18
|
+
userId: req.user._id,
|
|
19
|
+
status: 'active'
|
|
20
|
+
}).populate('orgId');
|
|
21
|
+
|
|
22
|
+
const orgs = memberships
|
|
23
|
+
.filter(m => m.orgId && m.orgId.status === 'active')
|
|
24
|
+
.map(m => ({
|
|
25
|
+
...m.orgId.toJSON(),
|
|
26
|
+
myRole: m.role
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
res.json({ orgs });
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error('Error listing orgs:', error);
|
|
32
|
+
res.status(500).json({ error: 'Failed to list organizations' });
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
exports.listPublicOrgs = async (req, res) => {
|
|
37
|
+
try {
|
|
38
|
+
const orgs = await Organization.find({ status: 'active' })
|
|
39
|
+
.select('name slug description allowPublicJoin createdAt')
|
|
40
|
+
.sort({ createdAt: -1 })
|
|
41
|
+
.lean();
|
|
42
|
+
|
|
43
|
+
res.json({
|
|
44
|
+
orgs: orgs.map((o) => ({
|
|
45
|
+
_id: o._id,
|
|
46
|
+
name: o.name,
|
|
47
|
+
slug: o.slug,
|
|
48
|
+
description: o.description,
|
|
49
|
+
allowPublicJoin: o.allowPublicJoin,
|
|
50
|
+
createdAt: o.createdAt
|
|
51
|
+
}))
|
|
52
|
+
});
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error('Error listing public orgs:', error);
|
|
55
|
+
res.status(500).json({ error: 'Failed to list organizations' });
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
exports.createOrg = async (req, res) => {
|
|
60
|
+
try {
|
|
61
|
+
const { name, description, slug: customSlug, allowPublicJoin } = req.body;
|
|
62
|
+
|
|
63
|
+
if (!name || name.trim().length === 0) {
|
|
64
|
+
return res.status(400).json({ error: 'Name is required' });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const slug = customSlug || generateSlug(name);
|
|
68
|
+
|
|
69
|
+
const existingOrg = await Organization.findOne({ slug });
|
|
70
|
+
if (existingOrg) {
|
|
71
|
+
return res.status(409).json({ error: 'An organization with this slug already exists' });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const org = await Organization.create({
|
|
75
|
+
name: name.trim(),
|
|
76
|
+
slug,
|
|
77
|
+
description: description?.trim(),
|
|
78
|
+
ownerUserId: req.user._id,
|
|
79
|
+
allowPublicJoin: allowPublicJoin || false
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await OrganizationMember.create({
|
|
83
|
+
orgId: org._id,
|
|
84
|
+
userId: req.user._id,
|
|
85
|
+
role: 'owner',
|
|
86
|
+
addedByUserId: req.user._id
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
res.status(201).json({
|
|
90
|
+
message: 'Organization created successfully',
|
|
91
|
+
org: { ...org.toJSON(), myRole: 'owner' }
|
|
92
|
+
});
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error('Error creating org:', error);
|
|
95
|
+
res.status(500).json({ error: 'Failed to create organization' });
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
exports.getOrg = async (req, res) => {
|
|
100
|
+
try {
|
|
101
|
+
res.json({ org: { ...req.org.toJSON(), myRole: req.orgMember?.role } });
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error('Error getting org:', error);
|
|
104
|
+
res.status(500).json({ error: 'Failed to get organization' });
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
exports.updateOrg = async (req, res) => {
|
|
109
|
+
try {
|
|
110
|
+
const { name, description, allowPublicJoin } = req.body;
|
|
111
|
+
const updates = {};
|
|
112
|
+
|
|
113
|
+
if (name !== undefined) updates.name = name.trim();
|
|
114
|
+
if (description !== undefined) updates.description = description?.trim();
|
|
115
|
+
if (allowPublicJoin !== undefined) updates.allowPublicJoin = allowPublicJoin;
|
|
116
|
+
|
|
117
|
+
const org = await Organization.findByIdAndUpdate(
|
|
118
|
+
req.org._id,
|
|
119
|
+
updates,
|
|
120
|
+
{ new: true, runValidators: true }
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
res.json({
|
|
124
|
+
message: 'Organization updated successfully',
|
|
125
|
+
org: { ...org.toJSON(), myRole: req.orgMember.role }
|
|
126
|
+
});
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error('Error updating org:', error);
|
|
129
|
+
res.status(500).json({ error: 'Failed to update organization' });
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
exports.deleteOrg = async (req, res) => {
|
|
134
|
+
try {
|
|
135
|
+
await Organization.findByIdAndUpdate(req.org._id, { status: 'disabled' });
|
|
136
|
+
res.json({ message: 'Organization disabled successfully' });
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error('Error deleting org:', error);
|
|
139
|
+
res.status(500).json({ error: 'Failed to disable organization' });
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
exports.listMembers = async (req, res) => {
|
|
144
|
+
try {
|
|
145
|
+
const members = await OrganizationMember.find({
|
|
146
|
+
orgId: req.org._id,
|
|
147
|
+
status: 'active'
|
|
148
|
+
}).populate('userId', 'email name');
|
|
149
|
+
|
|
150
|
+
res.json({
|
|
151
|
+
members: members.map(m => ({
|
|
152
|
+
_id: m._id,
|
|
153
|
+
userId: m.userId._id,
|
|
154
|
+
email: m.userId.email,
|
|
155
|
+
name: m.userId.name,
|
|
156
|
+
role: m.role,
|
|
157
|
+
createdAt: m.createdAt
|
|
158
|
+
}))
|
|
159
|
+
});
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.error('Error listing members:', error);
|
|
162
|
+
res.status(500).json({ error: 'Failed to list members' });
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
exports.addMember = async (req, res) => {
|
|
167
|
+
try {
|
|
168
|
+
const defaultRole = await getDefaultOrgRole();
|
|
169
|
+
const { email, role = defaultRole, sendNotification = false } = req.body;
|
|
170
|
+
|
|
171
|
+
if (!email) {
|
|
172
|
+
return res.status(400).json({ error: 'Email is required' });
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!(await isValidOrgRole(role))) {
|
|
176
|
+
const allowed = await getAllowedOrgRoles();
|
|
177
|
+
return res.status(400).json({ error: 'Invalid role', allowedRoles: allowed });
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const user = await User.findOne({ email: email.toLowerCase() });
|
|
181
|
+
if (!user) {
|
|
182
|
+
return res.status(404).json({ error: 'User not found. Use invite instead.' });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const existingMember = await OrganizationMember.findOne({
|
|
186
|
+
orgId: req.org._id,
|
|
187
|
+
userId: user._id
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
if (existingMember) {
|
|
191
|
+
if (existingMember.status === 'active') {
|
|
192
|
+
return res.status(409).json({ error: 'User is already a member' });
|
|
193
|
+
}
|
|
194
|
+
existingMember.status = 'active';
|
|
195
|
+
existingMember.role = role;
|
|
196
|
+
existingMember.addedByUserId = req.user._id;
|
|
197
|
+
await existingMember.save();
|
|
198
|
+
} else {
|
|
199
|
+
await OrganizationMember.create({
|
|
200
|
+
orgId: req.org._id,
|
|
201
|
+
userId: user._id,
|
|
202
|
+
role,
|
|
203
|
+
addedByUserId: req.user._id
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (sendNotification) {
|
|
208
|
+
try {
|
|
209
|
+
await emailService.sendEmail({
|
|
210
|
+
to: user.email,
|
|
211
|
+
subject: `You've been added to ${req.org.name}`,
|
|
212
|
+
html: `<p>Hello${user.name ? ' ' + user.name : ''},</p>
|
|
213
|
+
<p>You have been added to <strong>${req.org.name}</strong> as a ${role}.</p>
|
|
214
|
+
<p>Log in to access the organization.</p>`
|
|
215
|
+
});
|
|
216
|
+
} catch (emailError) {
|
|
217
|
+
console.error('Failed to send notification email:', emailError);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
res.status(201).json({
|
|
222
|
+
message: 'Member added successfully',
|
|
223
|
+
member: { userId: user._id, email: user.email, name: user.name, role }
|
|
224
|
+
});
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.error('Error adding member:', error);
|
|
227
|
+
res.status(500).json({ error: 'Failed to add member' });
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
exports.updateMemberRole = async (req, res) => {
|
|
232
|
+
try {
|
|
233
|
+
const { userId } = req.params;
|
|
234
|
+
const { role } = req.body;
|
|
235
|
+
|
|
236
|
+
if (!(await isValidOrgRole(role))) {
|
|
237
|
+
const allowed = await getAllowedOrgRoles();
|
|
238
|
+
return res.status(400).json({ error: 'Invalid role', allowedRoles: allowed });
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const member = await OrganizationMember.findOne({
|
|
242
|
+
orgId: req.org._id,
|
|
243
|
+
userId,
|
|
244
|
+
status: 'active'
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
if (!member) {
|
|
248
|
+
return res.status(404).json({ error: 'Member not found' });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (member.role === 'owner') {
|
|
252
|
+
return res.status(403).json({ error: 'Cannot change owner role' });
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
member.role = role;
|
|
256
|
+
await member.save();
|
|
257
|
+
|
|
258
|
+
res.json({ message: 'Role updated successfully', member });
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.error('Error updating member role:', error);
|
|
261
|
+
res.status(500).json({ error: 'Failed to update role' });
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
exports.removeMember = async (req, res) => {
|
|
266
|
+
try {
|
|
267
|
+
const { userId } = req.params;
|
|
268
|
+
|
|
269
|
+
const member = await OrganizationMember.findOne({
|
|
270
|
+
orgId: req.org._id,
|
|
271
|
+
userId,
|
|
272
|
+
status: 'active'
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
if (!member) {
|
|
276
|
+
return res.status(404).json({ error: 'Member not found' });
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (member.role === 'owner') {
|
|
280
|
+
return res.status(403).json({ error: 'Cannot remove owner' });
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (userId === req.user._id.toString()) {
|
|
284
|
+
return res.status(400).json({ error: 'Cannot remove yourself' });
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
member.status = 'removed';
|
|
288
|
+
await member.save();
|
|
289
|
+
|
|
290
|
+
res.json({ message: 'Member removed successfully' });
|
|
291
|
+
} catch (error) {
|
|
292
|
+
console.error('Error removing member:', error);
|
|
293
|
+
res.status(500).json({ error: 'Failed to remove member' });
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
exports.joinOrg = async (req, res) => {
|
|
298
|
+
try {
|
|
299
|
+
if (!req.org.allowPublicJoin) {
|
|
300
|
+
return res.status(403).json({ error: 'This organization does not allow public join' });
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const defaultRole = await getDefaultOrgRole();
|
|
304
|
+
|
|
305
|
+
const existingMember = await OrganizationMember.findOne({
|
|
306
|
+
orgId: req.org._id,
|
|
307
|
+
userId: req.user._id
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
if (existingMember) {
|
|
311
|
+
if (existingMember.status === 'active') {
|
|
312
|
+
return res.status(409).json({ error: 'You are already a member' });
|
|
313
|
+
}
|
|
314
|
+
existingMember.status = 'active';
|
|
315
|
+
existingMember.role = defaultRole;
|
|
316
|
+
await existingMember.save();
|
|
317
|
+
} else {
|
|
318
|
+
await OrganizationMember.create({
|
|
319
|
+
orgId: req.org._id,
|
|
320
|
+
userId: req.user._id,
|
|
321
|
+
role: defaultRole
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
res.status(201).json({
|
|
326
|
+
message: 'Successfully joined organization',
|
|
327
|
+
org: { ...req.org.toJSON(), myRole: defaultRole }
|
|
328
|
+
});
|
|
329
|
+
} catch (error) {
|
|
330
|
+
console.error('Error joining org:', error);
|
|
331
|
+
res.status(500).json({ error: 'Failed to join organization' });
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
exports.getOrgPublic = async (req, res) => {
|
|
336
|
+
try {
|
|
337
|
+
const { orgId } = req.params;
|
|
338
|
+
const org = await Organization.findOne({ _id: orgId, status: 'active' });
|
|
339
|
+
|
|
340
|
+
if (!org) {
|
|
341
|
+
return res.status(404).json({ error: 'Organization not found' });
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
res.json({
|
|
345
|
+
org: {
|
|
346
|
+
_id: org._id,
|
|
347
|
+
name: org.name,
|
|
348
|
+
slug: org.slug,
|
|
349
|
+
description: org.description,
|
|
350
|
+
allowPublicJoin: org.allowPublicJoin
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
} catch (error) {
|
|
354
|
+
console.error('Error getting public org:', error);
|
|
355
|
+
res.status(500).json({ error: 'Failed to get organization' });
|
|
356
|
+
}
|
|
357
|
+
};
|