Add timer REST stuff
This commit is contained in:
parent
4dfc3129e3
commit
36412c9f58
@ -21,5 +21,14 @@ module.exports = {
|
|||||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||||
'@typescript-eslint/no-explicit-any': 'off',
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'no-unused-vars': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'warn', // or "error"
|
||||||
|
{
|
||||||
|
argsIgnorePattern: '^_',
|
||||||
|
varsIgnorePattern: '^_',
|
||||||
|
caughtErrorsIgnorePattern: '^_',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the `_FriendToTimer` table. If the table is not empty, all the data it contains will be lost.
|
||||||
|
- Added the required column `created_by_id` to the `Timer` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "_FriendToTimer" DROP CONSTRAINT "_FriendToTimer_A_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "_FriendToTimer" DROP CONSTRAINT "_FriendToTimer_B_fkey";
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Timer" ADD COLUMN "created_by_id" INTEGER NOT NULL;
|
||||||
|
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE "_FriendToTimer";
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "_referenced_friend_fk" (
|
||||||
|
"A" INTEGER NOT NULL,
|
||||||
|
"B" INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "_referenced_friend_fk_AB_unique" ON "_referenced_friend_fk"("A", "B");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "_referenced_friend_fk_B_index" ON "_referenced_friend_fk"("B");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Timer" ADD CONSTRAINT "Timer_created_by_id_fkey" FOREIGN KEY ("created_by_id") REFERENCES "Friend"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "_referenced_friend_fk" ADD CONSTRAINT "_referenced_friend_fk_A_fkey" FOREIGN KEY ("A") REFERENCES "Friend"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "_referenced_friend_fk" ADD CONSTRAINT "_referenced_friend_fk_B_fkey" FOREIGN KEY ("B") REFERENCES "Timer"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Timer" ALTER COLUMN "start" SET DEFAULT CURRENT_TIMESTAMP;
|
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Timer" ALTER COLUMN "start" DROP NOT NULL;
|
@ -19,30 +19,34 @@ model GodToken {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model Friend {
|
model Friend {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
name String @unique @db.Citext
|
name String @unique @db.Citext
|
||||||
public_key String @db.Text
|
public_key String @db.Text
|
||||||
|
|
||||||
timers Timer[]
|
referenced_in Timer[] @relation(name: "referenced_friend_fk")
|
||||||
|
created_timers Timer[] @relation(name: "created_by_fk")
|
||||||
god_tokens GodToken[]
|
god_tokens GodToken[]
|
||||||
TimerRefreshes TimerRefreshes[]
|
TimerRefreshes TimerRefreshes[]
|
||||||
}
|
}
|
||||||
|
|
||||||
model Timer {
|
model Timer {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
start DateTime
|
start DateTime? @default(now())
|
||||||
name String @unique
|
name String @unique
|
||||||
|
|
||||||
|
created_by Friend @relation(name: "created_by_fk", fields: [created_by_id], references: [id])
|
||||||
|
created_by_id Int
|
||||||
|
|
||||||
timer_refreshes TimerRefreshes[]
|
timer_refreshes TimerRefreshes[]
|
||||||
referenced_friends Friend[]
|
referenced_friends Friend[] @relation(name: "referenced_friend_fk")
|
||||||
}
|
}
|
||||||
|
|
||||||
model TimerRefreshes {
|
model TimerRefreshes {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
start DateTime
|
start DateTime
|
||||||
end DateTime
|
end DateTime
|
||||||
|
|
||||||
refreshed_by Friend @relation(fields: [refreshed_by_id], references: [id])
|
refreshed_by Friend @relation(fields: [refreshed_by_id], references: [id])
|
||||||
refreshed_by_id Int
|
refreshed_by_id Int
|
||||||
|
|
||||||
timer Timer @relation(fields: [timer_id], references: [id])
|
timer Timer @relation(fields: [timer_id], references: [id])
|
||||||
|
@ -10,20 +10,12 @@ import {
|
|||||||
UseGuards,
|
UseGuards,
|
||||||
Req,
|
Req,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { IsNotEmpty } from 'class-validator';
|
|
||||||
import { readKey, readCleartextMessage, verify } from 'openpgp';
|
import { readKey, readCleartextMessage, verify } from 'openpgp';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
import { AuthGuard } from './auth.guard';
|
import { AuthGuard } from './auth.guard';
|
||||||
|
|
||||||
export class LoginUserDTO {
|
import { RetrieveFriendDTO, SignedGodTokenDTO } from '../dto/dtos';
|
||||||
@IsNotEmpty()
|
|
||||||
signature: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class RetrieveTokenDTO {
|
|
||||||
@IsNotEmpty()
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Controller('/auth')
|
@Controller('/auth')
|
||||||
export class AuthController {
|
export class AuthController {
|
||||||
@ -40,7 +32,7 @@ export class AuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get('/')
|
@Get('/')
|
||||||
async retrieveGodToken(@Query() query: RetrieveTokenDTO) {
|
async makeGodToken(@Query() query: RetrieveFriendDTO) {
|
||||||
const friend = await this.authService.findFriendByName(query.name);
|
const friend = await this.authService.findFriendByName(query.name);
|
||||||
if (!friend) throw new NotFoundException('Friend not found with that name');
|
if (!friend) throw new NotFoundException('Friend not found with that name');
|
||||||
|
|
||||||
@ -50,7 +42,7 @@ export class AuthController {
|
|||||||
@Post()
|
@Post()
|
||||||
async verifyFriend(
|
async verifyFriend(
|
||||||
@Res({ passthrough: true }) res,
|
@Res({ passthrough: true }) res,
|
||||||
@Body() { signature }: LoginUserDTO,
|
@Body() { signature }: SignedGodTokenDTO,
|
||||||
) {
|
) {
|
||||||
let signatureObj;
|
let signatureObj;
|
||||||
try {
|
try {
|
||||||
|
@ -7,7 +7,7 @@ import { AuthService } from './auth.service';
|
|||||||
@Module({
|
@Module({
|
||||||
imports: [PrismaModule],
|
imports: [PrismaModule],
|
||||||
controllers: [AuthController],
|
controllers: [AuthController],
|
||||||
exports: [AuthGuard],
|
exports: [AuthGuard, AuthService],
|
||||||
providers: [AuthService, AuthGuard],
|
providers: [AuthService, AuthGuard],
|
||||||
})
|
})
|
||||||
export class AuthModule {}
|
export class AuthModule {}
|
||||||
|
16
server/src/dto/dtos.ts
Normal file
16
server/src/dto/dtos.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { IsNotEmpty } from 'class-validator';
|
||||||
|
|
||||||
|
export class SignedGodTokenDTO {
|
||||||
|
@IsNotEmpty()
|
||||||
|
signature: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RetrieveFriendDTO {
|
||||||
|
@IsNotEmpty()
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreateTimerDTO {
|
||||||
|
@IsNotEmpty()
|
||||||
|
name: string;
|
||||||
|
}
|
@ -1,4 +1,75 @@
|
|||||||
import { Controller } from '@nestjs/common';
|
import {
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Post,
|
||||||
|
Body,
|
||||||
|
Req,
|
||||||
|
Query,
|
||||||
|
NotFoundException,
|
||||||
|
UseGuards,
|
||||||
|
BadRequestException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { TimerService } from './timer.service';
|
||||||
|
import { AuthService } from '../auth/auth.service';
|
||||||
|
import { CreateTimerDTO, RetrieveFriendDTO } from '../dto/dtos';
|
||||||
|
import { AuthGuard } from 'src/auth/auth.guard';
|
||||||
|
import { Prisma } from '@prisma/client';
|
||||||
|
|
||||||
@Controller('timer')
|
@Controller('timers')
|
||||||
export class TimerController {}
|
@UseGuards(AuthGuard)
|
||||||
|
export class TimerController {
|
||||||
|
constructor(
|
||||||
|
private readonly timerService: TimerService,
|
||||||
|
private readonly authService: AuthService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
public async getAllTimers() {
|
||||||
|
return this.timerService.getAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('/friend')
|
||||||
|
public async getFriendTimers(@Query() { name }: RetrieveFriendDTO) {
|
||||||
|
const friend = await this.authService.findFriendByName(name);
|
||||||
|
if (!friend) throw new NotFoundException('Friend not found with that name');
|
||||||
|
return this.timerService.friendTimers(friend);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
public async createTimer(@Body() { name }: CreateTimerDTO, @Req() req) {
|
||||||
|
const referencedFriendIds = Array.from(
|
||||||
|
new Set(
|
||||||
|
[...name.matchAll(/\@<(\d+)>/g)].map(([_match, id]) =>
|
||||||
|
parseInt(id, 10),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (referencedFriendIds.length > 10)
|
||||||
|
throw new BadRequestException(
|
||||||
|
'Can link no more than 10 unique friends to timer',
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await this.timerService.createTimerWithFriends(
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
created_by: {
|
||||||
|
connect: {
|
||||||
|
id: req.friend.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
referencedFriendIds,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
if (
|
||||||
|
e instanceof Prisma.PrismaClientKnownRequestError &&
|
||||||
|
e.code === 'P2002'
|
||||||
|
)
|
||||||
|
throw new BadRequestException('Timer with name already exists');
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,9 +2,12 @@ import { Module } from '@nestjs/common';
|
|||||||
import { TimerGateway } from './timer.gateway';
|
import { TimerGateway } from './timer.gateway';
|
||||||
import { TimerController } from './timer.controller';
|
import { TimerController } from './timer.controller';
|
||||||
import { TimerService } from './timer.service';
|
import { TimerService } from './timer.service';
|
||||||
|
import { PrismaModule } from '../prisma/prisma.module';
|
||||||
|
import { AuthModule } from '../auth/auth.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
|
imports: [PrismaModule, AuthModule],
|
||||||
providers: [TimerGateway, TimerService],
|
providers: [TimerGateway, TimerService],
|
||||||
controllers: [TimerController]
|
controllers: [TimerController],
|
||||||
})
|
})
|
||||||
export class TimerModule {}
|
export class TimerModule {}
|
||||||
|
@ -1,4 +1,72 @@
|
|||||||
|
import { Friend, Prisma } from '@prisma/client';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TimerService {}
|
export class TimerService {
|
||||||
|
constructor(private readonly prismaService: PrismaService) {}
|
||||||
|
|
||||||
|
public getAll() {
|
||||||
|
return this.prismaService.timer.findMany({
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
start: true,
|
||||||
|
referenced_friends: {
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
id: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created_by: {
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
id: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public friendTimers(friend: Friend) {
|
||||||
|
return this.prismaService.timer.findMany({
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
start: true,
|
||||||
|
referenced_friends: {
|
||||||
|
where: {
|
||||||
|
id: friend.id,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created_by: {
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
id: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public createTimerWithFriends(
|
||||||
|
timer: Prisma.TimerCreateInput,
|
||||||
|
friendIds: number[],
|
||||||
|
) {
|
||||||
|
if (friendIds.length > 0)
|
||||||
|
return this.prismaService.timer.create({
|
||||||
|
data: {
|
||||||
|
...timer,
|
||||||
|
referenced_friends: {
|
||||||
|
connect: friendIds.map((id) => ({ id })),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return this.prismaService.timer.create({ data: timer });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user