Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions shatter-backend/docs/API_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,8 @@ Update a user's profile. Users can only update their own profile.
| `bio` | string | |
| `profilePhoto` | string | URL |
| `socialLinks` | object | `{ linkedin?, github?, other? }` |
| `organization` | string | Where the user works/studies |
| `title` | string | Job title or role |

**Success Response (200):**

Expand Down Expand Up @@ -755,8 +757,10 @@ Join an event as a guest (no account required).
| `socialLinks.linkedin` | string | No | LinkedIn URL |
| `socialLinks.github` | string | No | GitHub URL |
| `socialLinks.other` | string | No | Other URL |
| `organization` | string | No* | Where the guest works/studies |
| `title` | string | No | Job title or role |

\* At least one contact method is required: either `email` or at least one non-empty field in `socialLinks`.
\* At least one of the following is required: `email`, a non-empty field in `socialLinks`, or `organization`.

**Success Response (200):**

Expand All @@ -779,14 +783,14 @@ Join an event as a guest (no account required).
| Status | Error |
|--------|-------|
| 400 | `"Missing fields: guest name and eventId are required"` |
| 400 | `"At least one contact method is required (email or a social link)"` |
| 400 | `"At least one contact method (email or social link) or organization is required"` |
| 400 | `"Invalid email format"` |
| 400 | `"Event is full"` |
| 404 | `"Event not found"` |
| 409 | `"A user with this email already exists"` |

**Special Behavior:**
- Creates a guest User (`authProvider: 'guest'`) with the provided contact info (email and/or social links)
- Creates a guest User (`authProvider: 'guest'`) with the provided contact info (email, social links, and/or organization)
- If the display name is already taken in the event, a `#XXX` suffix is automatically appended (e.g., `John` becomes `John#472`). The response `participant.name` reflects the final display name, and the guest User's name is updated to match.
- Returns a JWT so the guest can make authenticated requests
- Guest can later upgrade to a full account via `PUT /api/users/:userId`
Expand Down Expand Up @@ -1259,7 +1263,8 @@ curl -X POST http://localhost:4000/api/events/<eventId>/join/user \
curl -X POST http://localhost:4000/api/events/<eventId>/join/guest \
-H "Content-Type: application/json" \
-d '{
"name": "Guest User"
"name": "Guest User",
"email": "guest@example.com"
}'
```

Expand Down
2 changes: 2 additions & 0 deletions shatter-backend/docs/DATABASE_SCHEMA.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
| `passwordHash` | String | No | — | `select: false` — excluded from queries by default |
| `linkedinId` | String | No | — | Unique (sparse) |
| `linkedinUrl` | String | No | — | Unique (sparse) |
| `organization` | String | No | — | Trimmed |
| `title` | String | No | — | Trimmed |
| `bio` | String | No | — | Trimmed |
| `profilePhoto` | String | No | — | |
| `socialLinks` | Object | No | — | `{ linkedin?: String, github?: String, other?: String }` |
Expand Down
13 changes: 9 additions & 4 deletions shatter-backend/src/controllers/event_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,10 +272,12 @@ export async function joinEventAsUser(req: Request, res: Response) {
*/
export async function joinEventAsGuest(req: Request, res: Response) {
try {
const { name, email, socialLinks } = req.body as {
const { name, email, socialLinks, organization, title } = req.body as {
name?: string;
email?: string;
socialLinks?: { linkedin?: string; github?: string; other?: string };
organization?: string;
title?: string;
};
const { eventId } = req.params;

Expand All @@ -286,18 +288,19 @@ export async function joinEventAsGuest(req: Request, res: Response) {
});
}

// Require at least one contact method
// Require at least one contact method or organization
const hasEmail = email && email.trim();
const hasSocialLink = socialLinks && (
socialLinks.linkedin?.trim() ||
socialLinks.github?.trim() ||
socialLinks.other?.trim()
);
const hasOrganization = organization && organization.trim();

if (!hasEmail && !hasSocialLink) {
if (!hasEmail && !hasSocialLink && !hasOrganization) {
return res.status(400).json({
success: false,
msg: "At least one contact method is required (email or a social link)",
msg: "At least one contact method (email or social link) or organization is required",
});
}

Expand Down Expand Up @@ -325,6 +328,8 @@ export async function joinEventAsGuest(req: Request, res: Response) {
authProvider: 'guest',
...(hasEmail && { email: email.toLowerCase().trim() }),
...(hasSocialLink && { socialLinks }),
...(hasOrganization && { organization: organization.trim() }),
...(title && title.trim() && { title: title.trim() }),
});

const userId = user._id as Types.ObjectId;
Expand Down
6 changes: 5 additions & 1 deletion shatter-backend/src/controllers/user_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,15 @@ export const updateUser = async (req: Request, res: Response) => {
return res.status(403).json({ success: false, error: "You can only update your own profile" });
}

const { name, email, password, bio, profilePhoto, socialLinks } = req.body as {
const { name, email, password, bio, profilePhoto, socialLinks, organization, title } = req.body as {
name?: string;
email?: string;
password?: string;
bio?: string;
profilePhoto?: string;
socialLinks?: { linkedin?: string; github?: string; other?: string };
organization?: string;
title?: string;
};

const updateFields: Record<string, any> = {};
Expand Down Expand Up @@ -161,6 +163,8 @@ export const updateUser = async (req: Request, res: Response) => {
if (bio !== undefined) updateFields.bio = bio;
if (profilePhoto !== undefined) updateFields.profilePhoto = profilePhoto;
if (socialLinks !== undefined) updateFields.socialLinks = socialLinks;
if (organization !== undefined) updateFields.organization = organization;
if (title !== undefined) updateFields.title = title;

if (Object.keys(updateFields).length === 0) {
return res.status(400).json({ success: false, error: "No fields to update" });
Expand Down
10 changes: 10 additions & 0 deletions shatter-backend/src/models/user_model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export interface IUser {
passwordHash?: string;
linkedinId?: string;
linkedinUrl?: string;
organization?: string;
title?: string;
bio?: string;
profilePhoto?: string;
socialLinks?: {
Expand Down Expand Up @@ -65,6 +67,14 @@ const UserSchema = new Schema<IUser>(
unique: true,
sparse: true,
},
organization: {
type: String,
trim: true,
},
title: {
type: String,
trim: true,
},
bio: {
type: String,
trim: true,
Expand Down
Loading