Skip to content

Commit 4ff36ca

Browse files
committed
update
1 parent e348a60 commit 4ff36ca

File tree

10 files changed

+245
-35
lines changed

10 files changed

+245
-35
lines changed

Diff for: backend/.dockerignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Dockerfile
2+
.dockerignore
3+
node_modules
4+
npm-debug.log
5+
dist

Diff for: backend/Dockerfile

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Use the official Node.js image as a base
2+
FROM node:18
3+
4+
# Create and change to the app directory
5+
WORKDIR /app
6+
7+
# Install app dependencies
8+
COPY package*.json ./
9+
RUN npm install
10+
11+
# Copy app source code
12+
COPY . .
13+
14+
# Build the app
15+
RUN npm run build
16+
17+
# Expose the port the app runs on
18+
EXPOSE 80
19+
20+
# Start the app
21+
CMD ["npm", "run", "start:prod"]

Diff for: backend/docker-compose.yml

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
services:
2+
thube-backend:
3+
depends_on:
4+
- redis
5+
image: thube.azurecr.io/thube-backend:latest
6+
container_name: thube-backend
7+
restart: always
8+
ports:
9+
- "80:80"
10+
env_file: .env
11+
networks:
12+
- thube-net
13+
14+
redis:
15+
image: "redis:alpine"
16+
container_name: redis
17+
restart: always
18+
ports:
19+
- "6379:6379"
20+
volumes:
21+
- "redis-data:/data"
22+
networks:
23+
- thube-net
24+
25+
volumes:
26+
redis-data:
27+
28+
networks:
29+
thube-net:
30+
driver: bridge

Diff for: backend/src/app.controller.ts

+48-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { AppService } from './app.service';
55
import { Account } from './database/schemas/account';
66
import { Stream } from './database/schemas/stream';
77
import { Paged } from './types';
8+
import { Video } from './database/schemas/video';
89

910
@Controller()
1011
export class AppController {
@@ -72,6 +73,32 @@ export class AppController {
7273
);
7374
}
7475

76+
@Post('/upload-video')
77+
uploadVideo(
78+
@Body() dto: Video
79+
): Promise<Video | null> {
80+
return this.appService.uploadVideo(
81+
dto.videoId,
82+
dto.creator as string,
83+
dto.name,
84+
dto.thumbnail,
85+
dto.collection,
86+
dto.playback_uri,
87+
dto.tips
88+
);
89+
}
90+
91+
@Post('/watch-video/:address')
92+
watchVideo(
93+
@Param('address') address: string,
94+
@Body() videoId: string
95+
): Promise<boolean> {
96+
return this.appService.watchVideo(
97+
address,
98+
videoId,
99+
);
100+
}
101+
75102
@Get('/streams')
76103
getStreams(
77104
@Query('page') page: number,
@@ -83,7 +110,7 @@ export class AppController {
83110
);
84111
}
85112

86-
@Get('/stream/:id')
113+
@Get('/streams/:id')
87114
getStream(
88115
@Param('id') streamId: string,
89116
): Promise<Stream | null> {
@@ -92,6 +119,26 @@ export class AppController {
92119
);
93120
}
94121

122+
@Get('/videos')
123+
getVideos(
124+
@Query('page') page: number,
125+
@Query('creator') creator: string | null
126+
): Promise<Paged<Video[]> | null> {
127+
return this.appService.getVideos(
128+
page,
129+
creator,
130+
);
131+
}
132+
133+
@Get('/videos/:id')
134+
getVideo(
135+
@Param('id') videoId: string,
136+
): Promise<Video | null> {
137+
return this.appService.getVideo(
138+
videoId
139+
);
140+
}
141+
95142
@Get('/accounts/:id')
96143
getAccount(
97144
@Param('address') address: string,

Diff for: backend/src/app.module.ts

+3
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import { BullModule } from '@nestjs/bullmq';
77
import { MongooseModule } from '@nestjs/mongoose';
88
import { ConfigModule } from '@nestjs/config';
99
import { Stream } from 'stream';
10+
import { VideoSchema } from './database/schemas/video';
1011
import { StreamSchema } from './database/schemas/stream';
1112
import { Account, AccountSchema } from './database/schemas/account';
1213
import { MailWorker } from './worker/mail';
1314
// import { ScheduleModule } from '@nestjs/schedule';
1415
import { MainGateway } from './socket/main.gateway';
16+
import { Video } from './database/schemas/video';
1517

1618
@Module({
1719
imports: [
@@ -28,6 +30,7 @@ import { MainGateway } from './socket/main.gateway';
2830
MongooseModule.forFeature([
2931
{ name: Stream.name, schema: StreamSchema },
3032
{ name: Account.name, schema: AccountSchema },
33+
{ name: Video.name, schema: VideoSchema },
3134
])
3235
],
3336
controllers: [AppController],

Diff for: backend/src/app.service.ts

+93
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Injectable } from '@nestjs/common';
66
import { Account } from './database/schemas/account';
77
import { Stream } from './database/schemas/stream';
88
import { Paged } from './types';
9+
import { Video } from './database/schemas/video';
910

1011
const TAKE_SIZE: number = 25;
1112

@@ -14,6 +15,7 @@ export class AppService {
1415
constructor(
1516
@InjectModel(Stream.name) private streamModel: Model<Stream>,
1617
@InjectModel(Account.name) private accountModel: Model<Account>,
18+
@InjectModel(Video.name) private videoModel: Model<Video>,
1719
) {
1820
}
1921

@@ -140,6 +142,59 @@ export class AppService {
140142
}
141143
}
142144

145+
async uploadVideo(
146+
videoId: string,
147+
address: string,
148+
name: string,
149+
thumbnail: string,
150+
collection: string,
151+
playback_uri: string | null,
152+
tips: boolean,
153+
): Promise<Video | null> {
154+
try {
155+
const video: Video = {
156+
videoId,
157+
name,
158+
thumbnail,
159+
creator: address,
160+
playback_uri,
161+
tips,
162+
collection,
163+
created_at: new Date(),
164+
viewers: [],
165+
views: 0
166+
};
167+
168+
return await this.videoModel.create(video);
169+
} catch (error) {
170+
console.error(error);
171+
return null;
172+
}
173+
}
174+
175+
async watchVideo(
176+
videoId: string,
177+
streamer: string
178+
): Promise<boolean> {
179+
try {
180+
await this.videoModel.updateOne({
181+
videoId
182+
}, {
183+
$addToSet: {
184+
viewers: streamer
185+
},
186+
$inc: {
187+
views: 1
188+
}
189+
});
190+
191+
return true;
192+
} catch (error) {
193+
console.error(error);
194+
return false;
195+
}
196+
}
197+
143198
async getStreams(
144199
page: number,
145200
creator?: string
@@ -178,6 +233,44 @@ export class AppService {
178233
}
179234
}
180235

236+
async getVideos(
237+
page: number,
238+
creator?: string
239+
): Promise<Paged<Video[]> | null> {
240+
try {
241+
const filter = (creator != 'undefined') ? { creator } : {};
242+
243+
const total = await this.videoModel.countDocuments(filter);
244+
245+
const data = await this.videoModel.find(filter)
246+
.limit(TAKE_SIZE * 1)
247+
.skip((page - 1) * TAKE_SIZE)
248+
.sort({ start_at: 'desc' })
249+
.populate(['creator'])
250+
.exec();
251+
252+
const lastPage = Math.ceil(total / TAKE_SIZE);
253+
254+
return { total, lastPage, data };
255+
} catch (error) {
256+
console.error(error);
257+
return null;
258+
}
259+
}
260+
261+
async getVideo(
262+
videoId: string
263+
): Promise<Video | null> {
264+
try {
265+
return this.videoModel.findOne({ videoId })
266+
.populate(['creator'])
267+
.exec();
268+
} catch (error) {
269+
console.error(error);
270+
return null;
271+
}
272+
}
273+
181274
async getAccount(
182275
address: string
183276
): Promise<Account | null> {

Diff for: backend/src/database/schemas/video.ts

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/* eslint-disable prettier/prettier */
2+
3+
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
4+
import { HydratedDocument, Types } from 'mongoose';
5+
import { Account } from './account';
6+
7+
export type VideoDocument = HydratedDocument<Video>;
8+
9+
@Schema()
10+
export class Video {
11+
@Prop({ required: true, _id: true, unique: true })
12+
videoId: string;
13+
14+
@Prop({ required: true })
15+
name: string;
16+
17+
@Prop({ required: true })
18+
thumbnail: string;
19+
20+
@Prop({ required: true, type: Types.ObjectId, ref: Account.name })
21+
creator: Account | string;
22+
23+
@Prop({ default: null })
24+
playback_uri: string | null;
25+
26+
@Prop({ required: true })
27+
tips: boolean;
28+
29+
@Prop({ required: true, type: [Types.ObjectId], ref: Account.name })
30+
viewers: Account[];
31+
32+
@Prop({ default: 0 })
33+
views: number;
34+
35+
@Prop({ default: null })
36+
collection: string | null;
37+
38+
@Prop({ required: true })
39+
created_at: Date;
40+
}
41+
42+
export const VideoSchema = SchemaFactory.createForClass(Video);

Diff for: backend/src/main.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { AppModule } from './app.module';
55

66
async function bootstrap() {
77
const app = await NestFactory.create(AppModule);
8-
await app.listen(3000);
8+
app.enableCors();
9+
await app.listen(80);
910
}
11+
1012
bootstrap();

Diff for: backend/test/app.e2e-spec.ts

-24
This file was deleted.

Diff for: backend/test/jest-e2e.json

-9
This file was deleted.

0 commit comments

Comments
 (0)