@@ -3,28 +3,11 @@ const express = require('express');
3
3
const multer = require ( 'multer' ) ;
4
4
const bencode = require ( 'bencode' ) ;
5
5
const crypto = require ( 'crypto' ) ;
6
- const { ulid } = require ( 'ulid' ) ;
7
6
const db = require ( './../db' ) ;
7
+ const validator = require ( './validator' ) ;
8
8
9
9
const app = express . Router ( ) ;
10
10
11
- const verificationTypes = {
12
- group : {
13
- album : {
14
- required : [ 'artist' , 'album' , 'original_release_year' ] ,
15
- allowed : [ 'artist' , 'album' , 'original_release_year' ] ,
16
- errorMessage : 'Album group is missing information.'
17
- }
18
- } ,
19
- release : {
20
- album : {
21
- required : [ 'release_type' , 'format' , 'bitrate' , 'media' ] ,
22
- allowed : [ 'release_type' , 'format' , 'bitrate' , 'media' ] ,
23
- errorMessage : 'Album is missing information.'
24
- }
25
- }
26
- } ;
27
-
28
11
/**
29
12
* Saves torrent file to disk
30
13
*
@@ -69,7 +52,7 @@ const hashTorrent = (torrentBuffer) => {
69
52
* @param torrent - torrent object
70
53
* @return {Object } torrent object with buffer and hash attached
71
54
*/
72
- const processTorrent = async ( torrent ) => {
55
+ const processTorrent = ( torrent ) => {
73
56
const decodedTorrent = bencode . decode ( torrent . buffer ) ;
74
57
delete decodedTorrent . announce ;
75
58
@@ -86,137 +69,153 @@ const processTorrent = async (torrent) => {
86
69
return torrent ;
87
70
} ;
88
71
89
- /**
90
- * Verifies an Object
91
- *
92
- * Object verified against an element of verificationTypes
93
- * Removes all extraneous fields on the input Object
94
- * Throws error if required fields are missing
95
- *
96
- * @param input - Object to be verified
97
- * @param type - Field of the verificationTypes object
98
- * @return {Promise<Map<torrent>> }
99
- */
100
- const verify = async ( input , type ) => {
101
- const { required } = type ;
102
- const { allowed } = type ;
103
- const errMsg = type . errorMessage || 'Missing required fields.' ;
104
-
105
- const verified = new Map ( ) ;
106
- Object . entries ( input ) . forEach ( ( entry ) => {
107
- const key = entry [ 0 ] ;
108
- const value = entry [ 1 ] ;
109
-
110
- if ( allowed . includes ( key ) ) {
111
- verified . set ( key , value ) ;
112
- }
113
- } ) ;
72
+ const torrentUpload = upload . fields ( [ { name : 'torrent' , maxCount : 1 } ] ) ;
114
73
115
- await Promise . all (
116
- required . map ( async ( field ) => {
117
- if ( ! verified . has ( field ) ) {
118
- throw new Error ( errMsg ) ;
74
+ app . post ( '/upload' , torrentUpload , async ( req , res ) => {
75
+ const release = JSON . parse ( req . body . release ) ;
76
+ const client = await db . pool . connect ( ) ;
77
+
78
+ if ( release . torrentType === 'music' ) {
79
+ const musicReleaseTypes = ( await db . getMusicReleaseTypes ( ) ) . rows ;
80
+ const musicQualities = ( await db . getMusicQualities ( ) ) . rows ;
81
+ try {
82
+ await validator . validateMusic ( {
83
+ release,
84
+ musicReleaseTypes,
85
+ musicQualities
86
+ } ) ;
87
+
88
+ // valid if doesn't throw
89
+ await Promise . all ( release . artists . map ( validator . validateArtist ) ) ;
90
+
91
+ await db . beginTransaction ( client ) ;
92
+
93
+ let musicId = release . music ;
94
+ // insert music if necessary
95
+ if ( release . music instanceof Object ) {
96
+ // music comes with at least 1 associated artist
97
+ // insert the artist(s) if they are not ids
98
+ const artistsToInsert = release . artists . filter ( ( artist ) =>
99
+ Object . prototype . hasOwnProperty . call ( artist , 'name' )
100
+ ) ;
101
+ const artistIds = release . artists . filter ( ( artist ) =>
102
+ Object . prototype . hasOwnProperty . call ( artist , 'id' )
103
+ ) ;
104
+ const insertedArtists = await db . insertArtists ( artistsToInsert , client ) ;
105
+ const artistsToLink = [ ...artistIds , ...insertedArtists ] ;
106
+
107
+ // insert music and assign it to musicId to be used to create the music_release
108
+ const insertMusicRes = await db . insertMusic (
109
+ {
110
+ music : release . music ,
111
+ artistsToLink
112
+ } ,
113
+ client
114
+ ) ;
115
+ musicId = insertMusicRes . rows [ 0 ] . id ;
119
116
}
120
- } )
121
- ) ;
122
-
123
- return verified ;
124
- } ;
125
-
126
- const upTo = ( to , startAt = 1 ) => {
127
- const upToArray = [ ] ;
128
- for ( let i = startAt ; i <= to ; i ++ ) {
129
- upToArray . push ( `$${ i } ` ) ;
130
- }
131
- return upToArray . toString ( ) ;
132
- } ;
133
-
134
- /**
135
- * Gets the group_id that the input torrent should use
136
- *
137
- * Either uses the user provided group number or creates a new group if the
138
- * user provides one, or creates a new group from the user provided data
139
- *
140
- * @param {Object } group - group user input
141
- * @return {Promise<number> } The group number associated with a torrent
142
- */
143
- const getGroup = async ( group ) => {
144
- if ( typeof group === 'number' ) {
145
- return group ;
146
- }
147
-
148
- const verifiedGroup = verify ( group , verificationTypes . group . album ) ;
149
- const keys = verifiedGroup . keys ( ) ;
150
- const values = verifiedGroup . values ( ) ;
151
- const length = verifiedGroup . size ;
152
-
153
- const res = await db . query (
154
- `insert into groups (${ [ ...keys ] . toString ( ) } ) values (${ upTo (
155
- length + 1
156
- ) } ) returning id`,
157
- [ ulid ( ) , ...values ]
158
- ) ;
159
- group = res . rows [ 0 ] . id ;
160
- return group ;
161
- } ;
162
-
163
- /**
164
- * Stores a release to the database
165
- *
166
- * Verifies the releaseInfo and adds all of the metadata that is obtained from
167
- * the other functions
168
- *
169
- * @async
170
- * @param {Object } torrent - slightly modified user inputted torrent
171
- * @param {number } group - group (user inputted id or new group id)
172
- * @param {Object } releaseInfo - extra user provided data
173
- * @return {Promise<Object> } The data associated with a persisted torrent
174
- */
175
- const store = async ( torrent , group , releaseInfo ) => {
176
- const verifiedRelease = await verify (
177
- releaseInfo ,
178
- verificationTypes . release . album
179
- ) ;
180
- // Add more information to the verified user input
181
- verifiedRelease . set ( 'group_id' , group ) ;
182
- verifiedRelease . set ( 'file_size' , torrent . totalFileSize ) ;
183
- verifiedRelease . set ( 'original_file_name' , torrent . originalname ) ;
184
- verifiedRelease . set ( 'file_path' , torrent . path ) ;
185
- verifiedRelease . set ( 'files' , JSON . stringify ( torrent . fileList ) ) ;
186
-
187
- const keys = verifiedRelease . keys ( ) ;
188
- const values = verifiedRelease . values ( ) ;
189
- const length = verifiedRelease . size ;
190
-
191
- const res = await db . query (
192
- `insert into torrents (${ [ ...keys ] . toString ( ) } ) values (${ upTo (
193
- length + 1
194
- ) } ) returning id, group_id`,
195
- [ ulid ( ) , ...values ]
196
- ) ;
197
- return {
198
- groupId : res . rows [ 0 ] . group_id ,
199
- torrentId : res . rows [ 0 ] . id
200
- } ;
201
- } ;
202
117
203
- const torrentUpload = upload . fields ( [ { name : 'torrent' , maxCount : 1 } ] ) ;
118
+ const processedTorrent = processTorrent ( req . files . torrent [ 0 ] ) ;
119
+
120
+ // insert torrent
121
+ const torrentRes = await db . insertTorrent (
122
+ {
123
+ fileSize : processedTorrent . totalFileSize ,
124
+ originalFileName : processedTorrent . originalname ,
125
+ filePath : '/' ,
126
+ files : processedTorrent . fileList ,
127
+ uploaderId : '01D91F1JSSW4X5DNKSVPD2J3EM'
128
+ } ,
129
+ client
130
+ ) ;
131
+
132
+ const torrentId = torrentRes . rows [ 0 ] . id ;
133
+
134
+ // insert music release
135
+ await db . insertMusicRelease (
136
+ { musicId, releaseInfo : release . info , torrentId } ,
137
+ client
138
+ ) ;
139
+
140
+ await db . commitTransaction ( client ) ;
141
+ } catch ( e ) {
142
+ await db . rollbackTransaction ( client ) ;
143
+ console . log ( e ) ;
144
+ return res . status ( 400 ) . json ( {
145
+ error : e
146
+ } ) ;
147
+ } finally {
148
+ client . release ( ) ;
149
+ }
150
+ } else if ( release . torrentType === 'movie' ) {
151
+ try {
152
+ await validator . validateMovie ( {
153
+ release
154
+ } ) ;
155
+
156
+ await db . beginTransaction ( client ) ;
157
+
158
+ let movieId = release . movie ;
159
+ if ( release . movie instanceof Object ) {
160
+ const movieRes = await db . insertMovie (
161
+ {
162
+ name : release . movie . name ,
163
+ description : release . movie . description ,
164
+ year : release . movie . year
165
+ } ,
166
+ client
167
+ ) ;
168
+
169
+ movieId = movieRes . rows [ 0 ] . id ;
170
+ }
204
171
205
- app . post ( '/upload' , torrentUpload , async ( req , res ) => {
206
- const incomingTorrent = req . files . torrent [ 0 ] ;
207
- const incomingGroup = JSON . parse ( req . body . group ) ;
208
- const releaseInfo = JSON . parse ( req . body . info ) ;
209
-
210
- try {
211
- const processedTorrent = processTorrent ( incomingTorrent ) ;
212
- const torrentWithPath = await saveToDisk ( processedTorrent ) ;
213
- const group = await getGroup ( incomingGroup ) ;
214
- const persistedTorrent = await store ( torrentWithPath , group , releaseInfo ) ;
215
- res . status ( 200 ) . send ( persistedTorrent ) ;
216
- } catch ( e ) {
217
- console . log ( e ) ;
218
- res . sendStatus ( 500 ) ;
172
+ const processedTorrent = processTorrent ( req . files . torrent [ 0 ] ) ;
173
+
174
+ // insert torrent
175
+ const torrentRes = await db . insertTorrent (
176
+ {
177
+ fileSize : processedTorrent . totalFileSize ,
178
+ originalFileName : processedTorrent . originalname ,
179
+ filePath : '/' ,
180
+ files : processedTorrent . fileList ,
181
+ uploaderId : '01D91F1JSSW4X5DNKSVPD2J3EM'
182
+ } ,
183
+ client
184
+ ) ;
185
+
186
+ const torrentId = torrentRes . rows [ 0 ] . id ;
187
+
188
+ await db . insertVideoRelease (
189
+ {
190
+ videoId : movieId ,
191
+ quality : release . info . quality ,
192
+ title : release . info . title ,
193
+ description : release . info . description ,
194
+ torrentId
195
+ } ,
196
+ client
197
+ ) ;
198
+
199
+ await db . commitTransaction ( client ) ;
200
+ } catch ( e ) {
201
+ await db . rollbackTransaction ( client ) ;
202
+ console . log ( e ) ;
203
+ return res . status ( 400 ) . json ( {
204
+ error : e
205
+ } ) ;
206
+ } finally {
207
+ client . release ( ) ;
208
+ }
209
+ } else if ( release . torrentType === 'tv' ) {
210
+ } else if ( release . torrentType === 'anime' ) {
211
+ } else if (
212
+ release . torrentType === 'software' ||
213
+ release . torrentType === 'video-game'
214
+ ) {
215
+ } else {
216
+ return res . sendStatus ( 400 ) ;
219
217
}
218
+ res . sendStatus ( 200 ) ;
220
219
} ) ;
221
220
222
221
module . exports = app ;
0 commit comments