Compare commits
	
		
			No commits in common. "master" and "master" have entirely different histories.
		
	
	
		
	
		
					 14 changed files with 369 additions and 677 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -4,4 +4,3 @@ built | ||||||
| config/config.json | config/config.json | ||||||
| *.log | *.log | ||||||
| .env | .env | ||||||
| storage/files/* |  | ||||||
|  | @ -1,8 +1,6 @@ | ||||||
|  |  | ||||||
| 
 | 
 | ||||||
| a database site for notitg modcharts, currently very very unfinished | a database site for notitg modcharts, currently very very unfinished, basically just a boilerplate | ||||||
| 
 |  | ||||||
| you can login with discord, upload and download files, thats about it, but im still proud of it |  | ||||||
| 
 | 
 | ||||||
| ## setup | ## setup | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										
											BIN
										
									
								
								assets/icon.ico
									
										
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/icon.ico
									
										
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 70 KiB | 
							
								
								
									
										682
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										682
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										18
									
								
								package.json
									
										
									
									
									
								
							
							
						
						
									
										18
									
								
								package.json
									
										
									
									
									
								
							|  | @ -12,24 +12,20 @@ | ||||||
|   "license": "MIT", |   "license": "MIT", | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@types/express": "github:types/express", |     "@types/express": "github:types/express", | ||||||
|     "adm-zip": "^0.5.1", |     "@types/mongoose": "^5.7.36", | ||||||
|     "axios": "^0.20.0", |     "axios": "^0.20.0", | ||||||
|     "connect-mongo": "^3.2.0", |  | ||||||
|     "dotenv": "^8.2.0", |     "dotenv": "^8.2.0", | ||||||
|     "express": "^4.17.1", |     "express": "^4.17.1", | ||||||
|     "express-fileupload": "^1.2.0", |     "express-fileupload": "^1.2.0", | ||||||
|     "express-session": "^1.17.1", |     "mongoose": "^5.10.2", | ||||||
|     "mongoose": "^5.11.8", |  | ||||||
|     "mongoose-int32": "^0.4.1", |     "mongoose-int32": "^0.4.1", | ||||||
|     "serve-favicon": "^2.5.0", |     "node-stream-zip": "^1.11.3", | ||||||
|     "typescript": "^4.1.3", |     "typescript": "^4.0.2", | ||||||
|     "uuid": "^8.3.2", |  | ||||||
|     "winston": "^3.3.3" |     "winston": "^3.3.3" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@types/express-session": "^1.17.3", |     "@typescript-eslint/eslint-plugin": "^4.0.1", | ||||||
|     "@typescript-eslint/eslint-plugin": "^4.11.0", |     "@typescript-eslint/parser": "^4.0.1", | ||||||
|     "@typescript-eslint/parser": "^4.11.0", |     "eslint": "^7.8.1" | ||||||
|     "eslint": "^7.16.0" |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,14 +1,14 @@ | ||||||
| <!DOCTYPE html> | <!DOCTYPE html> | ||||||
| <html lang="en"> | <html lang="en"> | ||||||
| 
 | 
 | ||||||
| 	<head> |     <head> | ||||||
| 		<meta charset="UTF-8"> |         <meta charset="UTF-8"> | ||||||
| 		<meta name="viewport" content="width=device-width, initial-scale=1.0"> |         <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||
| 		<title>Index</title> |         <title>Index</title> | ||||||
| 	</head> |     </head> | ||||||
| 
 | 
 | ||||||
| 	<body> |     <body> | ||||||
| 		<h1>Hi</h1> |         <h1>Hi</h1> | ||||||
| 	</body> |     </body> | ||||||
| 
 | 
 | ||||||
| </html> | </html> | ||||||
|  | @ -1,42 +1,36 @@ | ||||||
| <!DOCTYPE html> | <!DOCTYPE html> | ||||||
| <html lang="en"> | <html lang="en"> | ||||||
| 
 | 
 | ||||||
| 	<head> |     <head> | ||||||
| 		<meta charset="UTF-8"> |         <meta charset="UTF-8"> | ||||||
| 		<meta name="viewport" content="width=device-width, initial-scale=1.0"> |         <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||
| 		<title>list</title> |         <title>list</title> | ||||||
| 	</head> |     </head> | ||||||
| 
 | 
 | ||||||
| 	<body> |     <body> | ||||||
| 		<div id="doc-list"> |         <div id="doc-list"> | ||||||
| 		</div> |         </div> | ||||||
| 
 | 
 | ||||||
| 		<script src="https://cdn.jsdelivr.net/npm/axios@0.20.0/dist/axios.min.js"></script> |         <script src="https://cdn.jsdelivr.net/npm/axios@0.20.0/dist/axios.min.js"></script> | ||||||
| 		<script> |         <script> | ||||||
| 			axios.get('/api/list').then(({ data }) => { |             axios.get('/api/list').then(({ data }) => { | ||||||
| 				console.log(data); |                 console.log(data); | ||||||
| 				const el = document.getElementById('doc-list'); |                 const el = document.getElementById('doc-list'); | ||||||
| 				for (const doc of data) { |                 for (const doc of data) { | ||||||
| 					let p = document.createElement('p'); |                     let p = document.createElement('p'); | ||||||
| 					p.innerHTML = `<b>${doc.artist} - ${doc.title}</b> by ${doc.credit}\nuploaded by ${doc.uploaderJSON.username}#${doc.uploaderJSON.discriminator}\n<a href="files/${doc.id}.zip">download</a>`; |                     p.innerText = `${doc.artist} - ${doc.title} by ${doc.credit}`; | ||||||
|  |                     el.insertAdjacentElement('beforeend', p); | ||||||
| 
 | 
 | ||||||
| 					if (doc.editable) { |                     let charts = document.createElement('ul'); | ||||||
| 						p.innerHTML += ` <a href="../${doc.id}/edit">edit</a>` |                     for (const chart of doc.charts) { | ||||||
| 					} |                         let l = document.createElement('li'); | ||||||
| 
 |                         l.innerText = `${chart.difficulty} ${chart.rating} - ${chart.name}` | ||||||
| 					el.insertAdjacentElement('beforeend', p); |                         charts.insertAdjacentElement('beforeend', l); | ||||||
| 
 |                     } | ||||||
| 					let charts = document.createElement('ul'); |                     el.insertAdjacentElement('beforeend', charts); | ||||||
| 					for (const chart of doc.charts) { |                 } | ||||||
| 						let l = document.createElement('li'); |             }); | ||||||
| 						l.innerHTML = `${chart.difficulty} ${chart.rating} - <b>${chart.name}</b><br>` + |         </script> | ||||||
| 							`${chart.steps} steps, ${chart.mines} mines, ${chart.jumps} jumps, ${chart.hands} hands, ${chart.holds} holds, ${chart.rolls} rolls` |     </body> | ||||||
| 						charts.insertAdjacentElement('beforeend', l); |  | ||||||
| 					} |  | ||||||
| 					el.insertAdjacentElement('beforeend', charts); |  | ||||||
| 				} |  | ||||||
| 			}); |  | ||||||
| 		</script> |  | ||||||
| 	</body> |  | ||||||
| 
 | 
 | ||||||
| </html> | </html> | ||||||
							
								
								
									
										47
									
								
								src/auth.ts
									
										
									
									
									
								
							
							
						
						
									
										47
									
								
								src/auth.ts
									
										
									
									
									
								
							|  | @ -1,6 +1,3 @@ | ||||||
| import { User } from './schema'; |  | ||||||
| import * as uuid from 'uuid'; |  | ||||||
| 
 |  | ||||||
| const API_ENDPOINT = 'https://discord.com/api/v6'; | const API_ENDPOINT = 'https://discord.com/api/v6'; | ||||||
| 
 | 
 | ||||||
| const axios = require('axios').default; | const axios = require('axios').default; | ||||||
|  | @ -8,7 +5,6 @@ const axios = require('axios').default; | ||||||
| export function run(app) { | export function run(app) { | ||||||
| 	app.get('/discordauth', async (req, res) => { | 	app.get('/discordauth', async (req, res) => { | ||||||
| 		const code = req.query.code; | 		const code = req.query.code; | ||||||
| 		const url = `http://${req.headers.host}/discordauth`; |  | ||||||
| 
 | 
 | ||||||
| 		if (code) { | 		if (code) { | ||||||
| 			try { | 			try { | ||||||
|  | @ -17,7 +13,7 @@ export function run(app) { | ||||||
| 					client_secret: process.env.DISCORD_OAUTH_CLIENTSECRET, | 					client_secret: process.env.DISCORD_OAUTH_CLIENTSECRET, | ||||||
| 					grant_type: 'authorization_code', | 					grant_type: 'authorization_code', | ||||||
| 					code: code, | 					code: code, | ||||||
| 					redirect_uri: url, | 					redirect_uri: 'http://localhost:8080/discordauth', | ||||||
| 					scope: 'identify' | 					scope: 'identify' | ||||||
| 				}); | 				}); | ||||||
| 	 | 	 | ||||||
|  | @ -32,49 +28,12 @@ export function run(app) { | ||||||
| 						authorization: `${postRes.data.token_type} ${postRes.data.access_token}` | 						authorization: `${postRes.data.token_type} ${postRes.data.access_token}` | ||||||
| 					} | 					} | ||||||
| 				}); | 				}); | ||||||
| 
 | 				res.send(`hi ${userInfo.data.username}#${userInfo.data.discriminator}<br><img src="https://media.discordapp.net/avatars/${userInfo.data.id}/${userInfo.data.avatar}.png">`); | ||||||
| 				const users = await User.find({id: String(userInfo.data.id)}); |  | ||||||
| 				let userUuid = ''; |  | ||||||
| 
 |  | ||||||
| 				if (users.length === 0) { |  | ||||||
| 					let newUuid = uuid.v4(); |  | ||||||
| 
 |  | ||||||
| 					while (User.find({uuid: newUuid})[0]) { |  | ||||||
| 						newUuid = uuid.v4(); |  | ||||||
| 					} |  | ||||||
| 
 |  | ||||||
| 					const newUser = new User({ |  | ||||||
| 						id: String(userInfo.data.id), |  | ||||||
| 						createdAt: new Date(), |  | ||||||
| 
 |  | ||||||
| 						username: userInfo.data.username, |  | ||||||
| 						discriminator: userInfo.data.discriminator, |  | ||||||
| 						avatar: userInfo.data.avatar, |  | ||||||
| 
 |  | ||||||
| 						uuid: newUuid, |  | ||||||
| 					}); |  | ||||||
| 
 |  | ||||||
| 					userUuid = newUser.get('uuid'); |  | ||||||
| 					newUser.save(); |  | ||||||
| 				} else { |  | ||||||
| 					const user = users[0]; |  | ||||||
| 					userUuid = user.get('uuid'); |  | ||||||
| 
 |  | ||||||
| 					user.set('id', String(userInfo.data.id)); |  | ||||||
| 					user.set('username', userInfo.data.username); |  | ||||||
| 					user.set('discriminator', userInfo.data.discriminator); |  | ||||||
| 					user.set('avatar', userInfo.data.avatar); |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				req.session!.discord = userInfo.data; |  | ||||||
| 				req.session!.uuid = userUuid; |  | ||||||
| 				res.send(`logged in as ${userInfo.data.username}#${userInfo.data.discriminator}<br><img src="https://media.discordapp.net/avatars/${userInfo.data.id}/${userInfo.data.avatar}.png"><br>ur useruuid is ${userUuid}`); |  | ||||||
| 			} catch(err) { | 			} catch(err) { | ||||||
| 				res.send(`whoooops<br>${err}`); | 				res.send(`whoooops<br>${err}`); | ||||||
| 				console.error(err); |  | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			res.send(`<a href="https://discord.com/api/oauth2/authorize?client_id=${process.env.DISCORD_OAUTH_CLIENTID}&redirect_uri=${encodeURI(url)}&response_type=code&scope=identify">Click here!!</a>`); | 			res.send(`<a href="https://discord.com/api/oauth2/authorize?client_id=${process.env.DISCORD_OAUTH_CLIENTID}&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fdiscordauth&response_type=code&scope=identify">Click here!!</a>`); | ||||||
| 		} | 		} | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
							
								
								
									
										38
									
								
								src/index.ts
									
										
									
									
									
								
							
							
						
						
									
										38
									
								
								src/index.ts
									
										
									
									
									
								
							|  | @ -3,12 +3,9 @@ import * as mongoose from 'mongoose'; | ||||||
| import * as fs from 'fs'; | import * as fs from 'fs'; | ||||||
| import * as winston from 'winston'; | import * as winston from 'winston'; | ||||||
| import * as fileUpload from 'express-fileupload'; | import * as fileUpload from 'express-fileupload'; | ||||||
| import * as session from 'express-session'; |  | ||||||
| import * as favicon from 'serve-favicon'; |  | ||||||
| const MongoStore = require('connect-mongo')(session); |  | ||||||
| 
 | 
 | ||||||
| import * as format from './lib/format'; | import * as format from './lib/format'; | ||||||
| import { File, User } from './schema'; | import { File } from './schema'; | ||||||
| 
 | 
 | ||||||
| import * as upload from './upload'; | import * as upload from './upload'; | ||||||
| import * as auth from './auth'; | import * as auth from './auth'; | ||||||
|  | @ -55,22 +52,8 @@ db.then(() => { | ||||||
| 
 | 
 | ||||||
| 	// @ts-ignore
 | 	// @ts-ignore
 | ||||||
| 	app.use(express.urlencoded({extended: true})); | 	app.use(express.urlencoded({extended: true})); | ||||||
| 	app.use(favicon('assets/icon.ico')); |  | ||||||
| 	app.use(fileUpload({limits: { fileSize: 50 * 1024 * 1024 }})); | 	app.use(fileUpload({limits: { fileSize: 50 * 1024 * 1024 }})); | ||||||
| 	app.use(express.static('public', {extensions:  ['html', 'htm']})); | 	app.use(express.static('public', {extensions:  ['html', 'htm']})); | ||||||
| 	app.use(express.static('storage', {extensions:  ['zip']})); |  | ||||||
| 	app.use(session({ |  | ||||||
| 		name: 'funnyuserdata', |  | ||||||
| 		secret: 'wenis', |  | ||||||
| 		store: new MongoStore({ mongooseConnection: mongoose.connection }), |  | ||||||
| 		cookie: { |  | ||||||
| 			maxAge: 1000 * 60 * 60 * 24 * 365 * 10, // 10 years
 |  | ||||||
| 			httpOnly: true, |  | ||||||
| 			sameSite: 'lax', |  | ||||||
| 		}, |  | ||||||
| 		resave: false, |  | ||||||
| 		saveUninitialized: true |  | ||||||
| 	})); |  | ||||||
| 	app.use('/assets', express.static('assets')); | 	app.use('/assets', express.static('assets')); | ||||||
| 
 | 
 | ||||||
| 	app.set('db', db); | 	app.set('db', db); | ||||||
|  | @ -80,23 +63,8 @@ db.then(() => { | ||||||
| 	upload.run(app); | 	upload.run(app); | ||||||
| 	auth.run(app); | 	auth.run(app); | ||||||
| 
 | 
 | ||||||
| 	app.get('/api/list', async (req, res) => { | 	app.get('/api/list', async (req, res) => { // only for testing
 | ||||||
| 		const files = await File.find({}); | 		const docs = await File.find({}); | ||||||
| 
 |  | ||||||
| 		const docs = []; |  | ||||||
| 		for (const doc of files) { |  | ||||||
| 			const d: any = doc.toJSON(); |  | ||||||
| 
 |  | ||||||
| 			d.editable = false; |  | ||||||
| 			if (req.session) d.editable = req.session.uuid === d.uploader; |  | ||||||
| 
 |  | ||||||
| 			const user = await User.find({uuid: d.uploader}); |  | ||||||
| 			if (user) { |  | ||||||
| 				d.uploaderJSON = user[0].toJSON(); // this is built upon 20 layers of metajank and i despise it
 |  | ||||||
| 				docs.push(d); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// TODO: filter out _id and __v? possibly more
 | 		// TODO: filter out _id and __v? possibly more
 | ||||||
| 		res.send(docs); | 		res.send(docs); | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
|  | @ -1,4 +1,6 @@ | ||||||
| export function parseSM(data: string) { | export function parseSM(data: string) { | ||||||
|  | 	data = data.replace(/[\n\r]/g,''); | ||||||
|  | 
 | ||||||
| 	// steps
 | 	// steps
 | ||||||
| 	const difficulties = []; | 	const difficulties = []; | ||||||
| 	const steps = data.split('#NOTES:'); | 	const steps = data.split('#NOTES:'); | ||||||
|  | @ -16,25 +18,11 @@ export function parseSM(data: string) { | ||||||
| 				diff.rating = Number(stepsSplit[3]); | 				diff.rating = Number(stepsSplit[3]); | ||||||
| 				diff.radarvalues = stepsSplit[4].split(',').map(v => Number(v)); | 				diff.radarvalues = stepsSplit[4].split(',').map(v => Number(v)); | ||||||
| 
 | 
 | ||||||
| 				const chart = stepsSplit[5]; |  | ||||||
| 				diff.rawChart = chart; |  | ||||||
| 
 |  | ||||||
| 				diff.steps = chart.split(/[124]/g).length - 1; |  | ||||||
| 				diff.mines = chart.split('M').length - 1; |  | ||||||
| 				diff.jumps = chart.split(/[124]0{0,2}[124]/g).length - 1; |  | ||||||
| 				diff.hands = chart.split(/[124]0{0,1}[124]0{0,1}[124]/g).length - 1; |  | ||||||
| 				diff.holds = chart.split('2').length - 1; |  | ||||||
| 				diff.rolls = chart.split('4').length - 1; |  | ||||||
| 
 |  | ||||||
| 				diff.steps -= diff.jumps; // jumps are counted as 1 step
 |  | ||||||
| 
 |  | ||||||
| 				difficulties.push(diff); | 				difficulties.push(diff); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	data = data.replace(/[\n\r]/g,''); |  | ||||||
| 
 |  | ||||||
| 	// metadata
 | 	// metadata
 | ||||||
| 	const lines = data.split(';').filter(l => l.startsWith('#')); | 	const lines = data.split(';').filter(l => l.startsWith('#')); | ||||||
| 	const obj: any = {}; | 	const obj: any = {}; | ||||||
|  | @ -53,7 +41,7 @@ export function parseSM(data: string) { | ||||||
| 				const map = {}; | 				const map = {}; | ||||||
| 
 | 
 | ||||||
| 				for (const i in keys) { | 				for (const i in keys) { | ||||||
| 					map[String(Number(keys[i])).replace('.', ',')] = Number(values[i]); // afaik maps are only numbers?
 | 					map[Number(keys[i])] = Number(values[i]); // afaik maps are only numbers?
 | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				value = map; | 				value = map; | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								src/lib/util.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/lib/util.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | ||||||
|  | import * as fs from 'fs'; | ||||||
|  | 
 | ||||||
|  | export function returnStatic(page) { | ||||||
|  | 	return (req, res) => { | ||||||
|  | 		fs.readFile(`src/html/${page}`, 'utf8', (err, data) => { | ||||||
|  | 			if (err) throw err; | ||||||
|  | 			res.send(data); | ||||||
|  | 		}); | ||||||
|  | 	}; | ||||||
|  | } | ||||||
|  | @ -1,118 +1,39 @@ | ||||||
| /* eslint-disable no-unused-vars */ |  | ||||||
| import * as mongoose from 'mongoose'; | import * as mongoose from 'mongoose'; | ||||||
| 
 | 
 | ||||||
| const Schema = mongoose.Schema; | const Schema = mongoose.Schema; | ||||||
|   |   | ||||||
| export enum SMVersion { |  | ||||||
| 	OPENITG, |  | ||||||
| 	FUCKEXE, |  | ||||||
| 	NOTITG_V1, |  | ||||||
| 	NOTITG_V2, |  | ||||||
| 	NOTITG_V3, |  | ||||||
| 	NOTITG_V3_1, |  | ||||||
| 	NOTITG_V4, |  | ||||||
| 	NOTITG_V4_0_1, |  | ||||||
| 	STEPMANIA_3_95, |  | ||||||
| 	STEPMANIA_5_0, |  | ||||||
| 	STEPMANIA_5_1, |  | ||||||
| 	STEPMANIA_5_2, |  | ||||||
| 	STEPMANIA_5_3, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const Sample = new Schema({ | const Sample = new Schema({ | ||||||
| 	start: {type: Number, default: 0}, | 	start: {type: Number, default: 0}, | ||||||
| 	length: {type: Number, default: 0} | 	length: {type: Number, default: 0} | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const UserRating = new Schema({ |  | ||||||
| 	rating: {type: Number, default: 0}, |  | ||||||
| 	createdAt: Date, |  | ||||||
| 	user: {type: String, default: '00000000-0000-4000-a000-000000000000'} |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| const Chart = new Schema({ | const Chart = new Schema({ | ||||||
| 	type: {type: String, default: 'dance-single'}, | 	type: {type: String, default: 'dance-single'}, | ||||||
| 	name: {type: String, default: ''}, | 	name: {type: String, default: ''}, | ||||||
| 	difficulty: {type: String, default: 'Challenge'}, | 	difficulty: {type: String, default: 'Challenge'}, | ||||||
| 	radarvalues: [Number], |  | ||||||
| 
 |  | ||||||
| 	rating: {type: Number, default: 0}, | 	rating: {type: Number, default: 0}, | ||||||
| 	ratingsVote: {type: [UserRating], default: []}, | 	radarvalues: [Number] | ||||||
| 
 |  | ||||||
| 	spoilered: {type: Boolean, default: false}, |  | ||||||
| 	hidden: {type: Boolean, default: false}, |  | ||||||
| 
 |  | ||||||
| 	steps: {type: Number, default: 0}, |  | ||||||
| 	mines: {type: Number, default: 0}, |  | ||||||
| 	jumps: {type: Number, default: 0}, |  | ||||||
| 	hands: {type: Number, default: 0}, |  | ||||||
| 	holds: {type: Number, default: 0}, |  | ||||||
| 	rolls: {type: Number, default: 0}, |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| const Comment = new Schema({ |  | ||||||
| 	author: {type: String, default: '00000000-0000-4000-a000-000000000000'}, |  | ||||||
| 	createdAt: Date, |  | ||||||
| 	content: {type: String, default: ''} |  | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const FileSchema = new Schema({ | const FileSchema = new Schema({ | ||||||
| 	id: {type: Number, default: 0}, |  | ||||||
| 
 |  | ||||||
| 	title: {type: String, default: 'unknown'}, | 	title: {type: String, default: 'unknown'}, | ||||||
| 	titleTranslit: String, | 	titleTranslit: String, | ||||||
| 	artist: {type: String, default: 'unknown'}, | 	artist: {type: String, default: 'unknown'}, | ||||||
| 	artistTranslit: String, | 	artistTranslit: String, | ||||||
| 	subtitle: String, | 	subtitle: String, | ||||||
| 	subtitleTranslit: String, | 	subtitleTranslit: String, | ||||||
| 
 |  | ||||||
| 	credit: String, | 	credit: String, | ||||||
| 	uploader: {type: String, default: '00000000-0000-4000-a000-000000000000'}, | 	uploader: {type: String, default: '00000000-0000-4000-a000-000000000000'}, | ||||||
| 
 |  | ||||||
| 	sample: Sample, | 	sample: Sample, | ||||||
| 	bpms: {type: Object, default: {'0': 0}}, | 	bpms: {type: Object, default: {'0': 0}}, | ||||||
| 
 | 	charts: [Chart] | ||||||
| 	charts: {type: [Chart], default: []}, |  | ||||||
| 
 |  | ||||||
| 	description: {type: String, default: ''}, |  | ||||||
| 	createdAt: Date, |  | ||||||
| 	smVersion: {type: Number, default: 0}, // see SMVersion enum
 |  | ||||||
| 	ytLink: String, |  | ||||||
| 	customLink: String, |  | ||||||
| 	hidden: {type: Boolean, default: false}, |  | ||||||
| 
 |  | ||||||
| 	comments: {type: [Comment], default: []}, |  | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export const File = mongoose.model('File', FileSchema); | export const File = mongoose.model('File', FileSchema); | ||||||
| 
 | 
 | ||||||
| const UserSchema = new Schema({ // this is pretty much just a discord user lol
 | const UserSchema = new Schema({ // this is pretty much just a discord user lol
 | ||||||
| 	id: {type: String, default: 'notgiven!!!!!!!!!!!!'}, // discord id, cus longass number
 | 	id: String, // cus longass number
 | ||||||
| 	createdAt: Date, | 	approved: Boolean | ||||||
| 
 |  | ||||||
| 	// caching
 |  | ||||||
| 	username: {type: String, default: 'User'}, |  | ||||||
| 	discriminator: {type: String, default: '0000'}, |  | ||||||
| 	avatar: String, |  | ||||||
| 
 |  | ||||||
| 	// used internally
 |  | ||||||
| 	uuid: {type: String, default: '00000000-0000-4000-a000-000000000000'}, |  | ||||||
| 
 |  | ||||||
| 	approvedUpload: {type: Boolean, default: false}, |  | ||||||
| 	approvedRate: {type: Boolean, default: false}, |  | ||||||
| 	approvedComment: {type: Boolean, default: false}, |  | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export const User = mongoose.model('User', UserSchema); | export const User = mongoose.model('User', UserSchema); | ||||||
| 
 |  | ||||||
| const PackSchema = new Schema({ |  | ||||||
| 	author: {type: String, default: '00000000-0000-4000-a000-000000000000'}, |  | ||||||
| 	files: {type: [Number], default: []}, // ids
 |  | ||||||
| 	name: {type: String, default: 'Pack'}, |  | ||||||
| 	description: {type: String, default: ''}, |  | ||||||
| 	createdAt: Date, |  | ||||||
| 
 |  | ||||||
| 	hidden: {type: Boolean, default: false}, |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| export const Pack = mongoose.model('Pack', PackSchema); |  | ||||||
|  | @ -1,75 +1,54 @@ | ||||||
| import { tmpdir } from 'os'; | import { tmpdir } from 'os'; | ||||||
| import * as fs from 'fs'; | import * as fs from 'fs'; | ||||||
| import * as AdmZip from 'adm-zip'; | const StreamZip = require('node-stream-zip'); | ||||||
| 
 | 
 | ||||||
|  | import { returnStatic } from './lib/util'; | ||||||
| import { parseSM } from './lib/smparse'; | import { parseSM } from './lib/smparse'; | ||||||
| import { File, User } from './schema'; | import { File } from './schema'; | ||||||
| 
 | 
 | ||||||
| export function run(app) { | export function run(app) { | ||||||
| 	const logger = app.get('logger'); | 	const logger = app.get('logger'); | ||||||
| 	 | 	 | ||||||
| 	app.post('/api/upload', async (req, res) => { // only for testing, very abusable
 | 	app.post('/api/upload', async (req, res) => { // only for testing, very abusable
 | ||||||
| 		if (!req.files) return res.status(400).send('No files were given'); | 		if (!req.files) return res.status(400).send('No files were given'); | ||||||
| 		if (!req.session.uuid) return res.status(401).send('Not authorized, use /discordauth'); |  | ||||||
| 
 |  | ||||||
| 		const user = (await User.find({uuid: req.session.uuid}))[0]; |  | ||||||
| 		if (!user) return res.status(401).send('User doesn\'t exist, try re-logging in'); |  | ||||||
| 		if (!user.get('approvedUpload')) return res.status(403).send('Your account is not allowed to upload files! Contact a moderator to verify your account'); |  | ||||||
| 		 |  | ||||||
| 		const file = req.files.file; | 		const file = req.files.file; | ||||||
| 	 | 	 | ||||||
| 		if (file.mimetype !== 'application/zip' && file.mimetype !== 'application/x-zip-compressed') return res.status(400).send('Invalid filetype'); | 		if (file.mimetype !== 'application/zip' && file.mimetype !== 'application/x-zip-compressed') return res.status(400).send('Invalid filetype'); | ||||||
| 
 | 
 | ||||||
| 		const dir = tmpdir() + '/' + file.md5; | 		const dir = tmpdir() + '/' + file.md5; | ||||||
| 		fs.writeFile(dir, file.data, async (err) => { | 		fs.writeFile(dir, file.data, (err) => { | ||||||
| 			if (err) throw err; | 			if (err) throw err; | ||||||
| 
 | 
 | ||||||
| 			try { | 			const zip = new StreamZip({ | ||||||
| 				const zip = new AdmZip(dir); | 				file: dir, | ||||||
| 				const zipEntries = zip.getEntries(); | 				storeEntries: true | ||||||
|  | 			}); | ||||||
| 
 | 
 | ||||||
| 				const smFile: any = Object.values(zipEntries).find((f: any) => | 			zip.on('ready', () => { | ||||||
| 					!f.isDirectory && (f.entryName.endsWith('.sm')) | 				const smFile = Object.values(zip.entries()).find((f: any) => | ||||||
|  | 					!f.isDirectory && (f.name.endsWith('.sm')) | ||||||
| 				); | 				); | ||||||
| 
 | 
 | ||||||
| 				if (!smFile) { | 				if (!smFile) { | ||||||
| 					res.status(400).send('No .sm found'); | 					res.status(400).send('No .sm found'); | ||||||
| 				} else { | 				} else { | ||||||
| 					const data = smFile.getData().toString('utf8'); | 					const data = zip.entryDataSync((smFile as any).name); | ||||||
| 					const chart = parseSM(data.toString()); | 					const chart = parseSM(data.toString()); | ||||||
| 
 | 
 | ||||||
| 					logger.info(`${chart.artist} - ${chart.title} was just uploaded`); | 					logger.info(`${chart.artist} - ${chart.title} was just uploaded`); | ||||||
| 
 | 
 | ||||||
| 					let id = 0; | 					const file = new File(chart); | ||||||
| 					for (const f of await File.find({})) { | 					file.save(); | ||||||
| 						id = Math.max(Number(f.id), id); |  | ||||||
| 					} |  | ||||||
| 					chart.id = id + 1; |  | ||||||
| 
 | 
 | ||||||
| 					chart.uploader = req.session.uuid; | 					// TODO: filter out _id and __v? possibly more
 | ||||||
| 
 | 					res.send(chart); | ||||||
| 					chart.createdAt = new Date(); |  | ||||||
| 
 |  | ||||||
| 					fs.writeFile('./storage/files/' + (id + 1) + '.zip', file.data, (err) => { |  | ||||||
| 						if (err) throw err; |  | ||||||
| 
 |  | ||||||
| 						const file = new File(chart); |  | ||||||
| 						file.save(); |  | ||||||
| 
 |  | ||||||
| 						// TODO: filter out _id and __v? possibly more
 |  | ||||||
| 						res.send(chart); |  | ||||||
| 					}); |  | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
|  | 				zip.close(); | ||||||
| 				fs.unlink(dir, (err) => { | 				fs.unlink(dir, (err) => { | ||||||
| 					if (err) throw err; | 					if (err) throw err; | ||||||
| 				}); | 				}); | ||||||
| 			} catch(err) { | 			}); | ||||||
| 				logger.error(err.toString()); |  | ||||||
| 				console.error(err); |  | ||||||
| 				res.status(400); |  | ||||||
| 				res.send(err.toString()); |  | ||||||
| 			} |  | ||||||
| 		}); | 		}); | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue