Add team list and joining

This commit is contained in:
2026-03-11 18:06:22 +01:00
parent 216506070b
commit b86b71d29c
13 changed files with 1002 additions and 28 deletions

View File

@@ -88,6 +88,9 @@ app.post('/teams', authMiddleware, async (req: Request, res: Response) => {
}
try {
const team = await teamService.addTeam({name, tag, description: description ?? ''});
// @ts-ignore
const userId = req.user.id;
await teamService.addTeamMember(team.id, userId, 'owner');
res.status(201).send(team);
} catch (err) {
console.log(err);
@@ -148,6 +151,60 @@ app.get('/teams/:id/tournaments', async (req: Request, res: Response) => {
res.send(entries);
});
app.get('/users/me/teams', authMiddleware, async (req: Request, res: Response) => {
try {
// @ts-ignore
const userId = req.user.id;
const teams = await teamService.getTeamsByUserId(userId);
res.send(teams);
} catch (err) {
console.log(err);
res.status(400).send({error: 'Failed to get user teams'});
}
});
app.post('/teams/:id/members', authMiddleware, async (req: Request, res: Response) => {
try {
// @ts-ignore
const userId = req.user.id;
const teamId = +req.params.id;
const isInTeam = await teamService.isUserInTeam(teamId, userId);
if (isInTeam) {
return res.status(409).send({error: 'User is already a member of this team'});
}
const member = await teamService.addTeamMember(teamId, userId, 'member');
res.status(201).send(member);
} catch (err) {
console.log(err);
res.status(400).send({error: 'Failed to join team'});
}
});
app.delete('/teams/:id/members/me', authMiddleware, async (req: Request, res: Response) => {
try {
// @ts-ignore
const userId = req.user.id;
const teamId = +req.params.id;
await teamService.removeTeamMember(teamId, userId);
res.status(200).send({message: 'Left team successfully'});
} catch (err) {
console.log(err);
res.status(400).send({error: 'Failed to leave team'});
}
});
app.get('/teams/:id/members', async (req: Request, res: Response) => {
try {
const members = await teamService.getTeamMembers(+req.params.id);
res.send(members);
} catch (err) {
console.log(err);
res.status(400).send({error: 'Failed to get team members'});
}
});
app.post('/register', async (req: Request, res: Response) => {
const { username, password } = req.body;
if (!username || !password) {

View File

@@ -13,3 +13,11 @@ export interface TournamentTeam {
registeredAt: string;
}
export interface TeamMember {
id: number;
teamId: number;
userId: number;
role: 'owner' | 'member';
joinedAt: string;
}

View File

@@ -1,4 +1,4 @@
import { Team, TournamentTeam } from '../models/team';
import { Team, TournamentTeam, TeamMember } from '../models/team';
import { Database, RunResult } from 'sqlite3';
export class TeamService {
@@ -26,6 +26,18 @@ export class TeamService {
FOREIGN KEY (teamId) REFERENCES Teams (id) ON DELETE CASCADE,
UNIQUE (tournamentId, teamId)
)`);
this.db.run(`CREATE TABLE IF NOT EXISTS TeamMembers
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
teamId INTEGER NOT NULL,
userId INTEGER NOT NULL,
role TEXT NOT NULL DEFAULT 'member',
joinedAt TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (teamId) REFERENCES Teams (id) ON DELETE CASCADE,
FOREIGN KEY (userId) REFERENCES Users (id) ON DELETE CASCADE,
UNIQUE (teamId, userId)
)`);
});
}
@@ -145,5 +157,78 @@ export class TeamService {
);
});
}
addTeamMember(teamId: number, userId: number, role: 'owner' | 'member' = 'member'): Promise<TeamMember> {
return new Promise<TeamMember>((resolve, reject) => {
const stmt = this.db.prepare(`INSERT INTO TeamMembers (teamId, userId, role) VALUES (?, ?, ?)`);
stmt.run(teamId, userId, role, function (this: RunResult, err: Error | null) {
if (err) return reject(err);
resolve({
id: (this as any).lastID,
teamId,
userId,
role,
joinedAt: new Date().toISOString(),
});
});
stmt.finalize();
});
}
removeTeamMember(teamId: number, userId: number): Promise<void> {
return new Promise((resolve, reject) => {
this.db.run(
`DELETE FROM TeamMembers WHERE teamId = ? AND userId = ?`,
[teamId, userId],
(err: Error | null) => {
if (err) return reject(err);
resolve();
}
);
});
}
getTeamsByUserId(userId: number): Promise<Team[]> {
return new Promise<Team[]>((resolve, reject) => {
this.db.all(
`SELECT t.*, tm.role, tm.joinedAt FROM Teams t
INNER JOIN TeamMembers tm ON t.id = tm.teamId
WHERE tm.userId = ?`,
[userId],
(err: Error | null, rows: Team[]) => {
if (err) return reject(err);
resolve(rows);
}
);
});
}
getTeamMembers(teamId: number): Promise<TeamMember[]> {
return new Promise<TeamMember[]>((resolve, reject) => {
this.db.all(
`SELECT tm.*, u.username FROM TeamMembers tm
INNER JOIN Users u ON tm.userId = u.id
WHERE tm.teamId = ?`,
[teamId],
(err: Error | null, rows: TeamMember[]) => {
if (err) return reject(err);
resolve(rows);
}
);
});
}
isUserInTeam(teamId: number, userId: number): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
this.db.get(
`SELECT COUNT(*) as count FROM TeamMembers WHERE teamId = ? AND userId = ?`,
[teamId, userId],
(err: Error | null, row: any) => {
if (err) return reject(err);
resolve(row.count > 0);
}
);
});
}
}

View File

@@ -90,4 +90,17 @@ export class UserService {
);
});
}
getUserByUsername(username: string): Promise<User | undefined> {
return new Promise((resolve, reject) => {
this.db.get(
'SELECT id, username FROM Users WHERE username = ?',
[username],
(err: Error | null, user: User | undefined) => {
if (err) return reject(err);
resolve(user);
}
);
});
}
}