Revert "Revert "Add file model""
This reverts commit 97733da8fbccc21bbe12848687834a34bcfc3f91.
This commit is contained in:
parent
97733da8fb
commit
162235a1d3
10
src/models/FileModel.js
Normal file
10
src/models/FileModel.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import mongoose, { Schema } from "mongoose";
|
||||||
|
|
||||||
|
const schema = new Schema({
|
||||||
|
uploader: mongoose.Types.ObjectId,
|
||||||
|
fileId: String,
|
||||||
|
uploadDate: Date,
|
||||||
|
contentType: String,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default mongoose.models.File || mongoose.model("File", schema);
|
@ -1,9 +1,15 @@
|
|||||||
import mongoose, { Schema } from "mongoose";
|
import mongoose, { Schema } from "mongoose";
|
||||||
|
|
||||||
const schema = new Schema({
|
const schema = new Schema({
|
||||||
email: String,
|
// The username of the user
|
||||||
username: String,
|
username: {
|
||||||
password: String,
|
type: String,
|
||||||
|
index: true,
|
||||||
|
},
|
||||||
|
password: String, // The hashed password of the user
|
||||||
|
salt: String, // The salt the password was hashed with
|
||||||
|
uploadKey: String, // The users upload key for ShareX
|
||||||
|
lastLoginDate: Date, // The last time the user logged in
|
||||||
});
|
});
|
||||||
|
|
||||||
export default mongoose.models.User || mongoose.model("User", schema);
|
export default mongoose.models.User || mongoose.model("User", schema);
|
||||||
|
@ -1,19 +1,47 @@
|
|||||||
import NextAuth from "next-auth";
|
import NextAuth from "next-auth";
|
||||||
import CredentialsProvider from "next-auth/providers/credentials";
|
import CredentialsProvider from "next-auth/providers/credentials";
|
||||||
|
import { connectMongo } from "../../../utils/helpers/mongoHelpers";
|
||||||
|
import {
|
||||||
|
generateRandomPassword,
|
||||||
|
isValidPassword,
|
||||||
|
} from "../../../utils/helpers/passwordHelpers";
|
||||||
|
import { createUser, getUser } from "../../../utils/helpers/userHelpers";
|
||||||
|
|
||||||
|
// Create admin account if one doesn't exist yet
|
||||||
|
const pass = generateRandomPassword();
|
||||||
|
createUser("admin", pass).then((returned) => {
|
||||||
|
if (returned === true) {
|
||||||
|
console.log(`Created admin account. Username: admin, Password: ${pass}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export const authOptions = {
|
export const authOptions = {
|
||||||
providers: [
|
providers: [
|
||||||
CredentialsProvider({
|
CredentialsProvider({
|
||||||
name: "Credentials",
|
name: "Credentials",
|
||||||
credentials: {
|
credentials: {
|
||||||
username: { label: "Username", type: "text", placeholder: "admin" },
|
username: {
|
||||||
|
label: "Username",
|
||||||
|
type: "text",
|
||||||
|
placeholder: "admin",
|
||||||
|
},
|
||||||
password: { label: "Password", type: "password" },
|
password: { label: "Password", type: "password" },
|
||||||
},
|
},
|
||||||
async authorize(credentials, req) {
|
async authorize(credentials, req) {
|
||||||
console.log(credentials);
|
await connectMongo();
|
||||||
const user = { id: "1", name: "J Smith", email: "admin@example.com" };
|
const { username, password } = credentials;
|
||||||
|
const user = await getUser(username);
|
||||||
|
if (user == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
|
const validPassword = isValidPassword(password, user.password);
|
||||||
|
if (validPassword === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
user.lastLoginDate = new Date();
|
||||||
|
await user.save();
|
||||||
return user;
|
return user;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
|
||||||
|
|
||||||
export default function handler(req, res) {
|
|
||||||
res.status(200).json({ name: 'John Doe' })
|
|
||||||
}
|
|
52
src/pages/api/upload/sharex.js
Normal file
52
src/pages/api/upload/sharex.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import multer from "multer";
|
||||||
|
import nextConnect from "next-connect";
|
||||||
|
import { createFile } from "../../../utils/helpers/fileHelpers";
|
||||||
|
import { getUserByUploadKey } from "../../../utils/helpers/userHelpers";
|
||||||
|
|
||||||
|
const apiRoute = nextConnect({
|
||||||
|
onError(error, req, res) {
|
||||||
|
res.status(501).json({
|
||||||
|
message: `An internal server error has occured. Please check console.`,
|
||||||
|
});
|
||||||
|
console.log(error);
|
||||||
|
},
|
||||||
|
onNoMatch(req, res) {
|
||||||
|
res.status(405).json({ message: `Method "${req.method}" Not Allowed` });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
apiRoute.use(multer().any());
|
||||||
|
|
||||||
|
apiRoute.post(async (req, res) => {
|
||||||
|
const file = req.files[0];
|
||||||
|
if (!file) {
|
||||||
|
return res.status(200).json({
|
||||||
|
status: "OK",
|
||||||
|
message: `No file provided`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const { originalname: filename, mimetype, buffer, size } = file;
|
||||||
|
const { secret } = req.body;
|
||||||
|
console.log(secret);
|
||||||
|
|
||||||
|
const user = await getUserByUploadKey(secret);
|
||||||
|
if (user == null) {
|
||||||
|
return res.status(200).json({
|
||||||
|
status: "OK",
|
||||||
|
message: `Unauthorized`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = await createFile(user, filename, buffer, mimetype);
|
||||||
|
res.status(200).json({
|
||||||
|
message: `${process.env.NEXT_PUBLIC_SITE_URL}/files/${id}`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default apiRoute;
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
api: {
|
||||||
|
bodyParser: false, // Disallow body parsing, consume as stream
|
||||||
|
},
|
||||||
|
};
|
37
src/pages/files/[fileId].js
Normal file
37
src/pages/files/[fileId].js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import FileModel from "../../models/FileModel";
|
||||||
|
import UserModel from "../../models/UserModel";
|
||||||
|
|
||||||
|
export default function File(props) {
|
||||||
|
const { isValidFile, fileData } = props;
|
||||||
|
const file = JSON.parse(fileData);
|
||||||
|
|
||||||
|
console.log(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getServerSideProps(ctx) {
|
||||||
|
let { fileId } = ctx.query;
|
||||||
|
fileId = fileId.split(".")[0];
|
||||||
|
|
||||||
|
const file = await FileModel.aggregate([
|
||||||
|
{
|
||||||
|
$match: {
|
||||||
|
fileId: fileId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$lookup: {
|
||||||
|
from: UserModel.collection.name,
|
||||||
|
localField: "uploader",
|
||||||
|
foreignField: "_id",
|
||||||
|
as: "uploader",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]).exec();
|
||||||
|
console.log(file.uploader);
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
isValidFile: file !== null,
|
||||||
|
fileData: JSON.stringify(file || []),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
44
src/utils/helpers/fileHelpers.js
Normal file
44
src/utils/helpers/fileHelpers.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import path from "path";
|
||||||
|
import { FILE_STORAGE_LOCATION } from "../../consts/filePaths";
|
||||||
|
import FileModel from "../../models/FileModel";
|
||||||
|
import { createFileIO } from "./ioHelpers";
|
||||||
|
import { connectMongo } from "./mongoHelpers";
|
||||||
|
import { randomString } from "./stringHelpers";
|
||||||
|
|
||||||
|
connectMongo();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the the files object in mongo for the given id
|
||||||
|
*
|
||||||
|
* @param {string} fileId The files id
|
||||||
|
* @return The file object or null if not found
|
||||||
|
*/
|
||||||
|
export async function getFile(fileId) {
|
||||||
|
return await FileModel.findOne({ fileId: fileId });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the file object in mongo and stores it to the storage location
|
||||||
|
*
|
||||||
|
* @param {UserModel} uploader The user who uploaded the file
|
||||||
|
* @param {[]} fileData The file data for the upload
|
||||||
|
*/
|
||||||
|
export async function createFile(uploader, fileName, buffer, contentType) {
|
||||||
|
const fileId = randomString(process.env.FILE_ID_LENGTH);
|
||||||
|
const extention = fileName.split(".")[1].toLowerCase();
|
||||||
|
// Todo: Check if the file was actually saved to
|
||||||
|
// disk and create a return type so we can notify the user what happened
|
||||||
|
await createFileIO(
|
||||||
|
`${FILE_STORAGE_LOCATION}${path.sep}${uploader.uploadKey}`,
|
||||||
|
`${fileId}.${extention}`,
|
||||||
|
buffer
|
||||||
|
);
|
||||||
|
const file = await FileModel.create({
|
||||||
|
uploader: uploader._id,
|
||||||
|
fileId: fileId,
|
||||||
|
uploadDate: new Date(),
|
||||||
|
contentType: contentType,
|
||||||
|
});
|
||||||
|
await file.save();
|
||||||
|
return `${fileId}.${extention}`;
|
||||||
|
}
|
64
src/utils/helpers/ioHelpers.js
Normal file
64
src/utils/helpers/ioHelpers.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import fs from "fs";
|
||||||
|
import NodeCache from "node-cache";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
const existsCache = new NodeCache({
|
||||||
|
stdTTL: 300, // 5 minutes
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given file/directory exists
|
||||||
|
*
|
||||||
|
* @param {string} path The path to the file/directory
|
||||||
|
* @returns If the file/directory exists
|
||||||
|
*/
|
||||||
|
export function exists(path) {
|
||||||
|
if (existsCache.has(path)) {
|
||||||
|
return existsCache.get(path);
|
||||||
|
}
|
||||||
|
const exists = fs.existsSync(path);
|
||||||
|
existsCache.set(path, exists);
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a file in the given directory
|
||||||
|
*
|
||||||
|
* @param {string} path The path to the file
|
||||||
|
* @param {Buffer} bytes The bytes of the file
|
||||||
|
*/
|
||||||
|
export function createFileIO(dir, fileName, bytes) {
|
||||||
|
if (!exists(dir)) {
|
||||||
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileLocation = dir + path.sep + fileName;
|
||||||
|
console.log(fileLocation);
|
||||||
|
fs.writeFile(
|
||||||
|
fileLocation,
|
||||||
|
bytes,
|
||||||
|
{
|
||||||
|
encoding: "utf-8",
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a file in the given directory
|
||||||
|
*
|
||||||
|
* @param {string} path The path to the file
|
||||||
|
* @return The file
|
||||||
|
*/
|
||||||
|
export function readFileIO(path) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fs.readFile(path, (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
return resolve(data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
9
src/utils/helpers/mongoHelpers.js
Normal file
9
src/utils/helpers/mongoHelpers.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import mongoose from "mongoose";
|
||||||
|
|
||||||
|
export async function connectMongo() {
|
||||||
|
try {
|
||||||
|
await mongoose.connect(process.env.MONGODB_CONNECTION_STRING);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`Mongo connection failed: ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import bcrypt from "bcrypt";
|
import bcrypt from "bcrypt";
|
||||||
|
import { randomString } from "./stringHelpers";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a random salt for a password
|
* Generates a random salt for a password
|
||||||
@ -6,7 +7,7 @@ import bcrypt from "bcrypt";
|
|||||||
* @return The random salt
|
* @return The random salt
|
||||||
*/
|
*/
|
||||||
export function generateSalt() {
|
export function generateSalt() {
|
||||||
return randomString(16);
|
return bcrypt.genSaltSync(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,7 +16,7 @@ export function generateSalt() {
|
|||||||
* @return The password
|
* @return The password
|
||||||
*/
|
*/
|
||||||
export function generateRandomPassword() {
|
export function generateRandomPassword() {
|
||||||
return randomString(8);
|
return randomString(16);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,10 +33,10 @@ export function hashPassword(salt, password) {
|
|||||||
/**
|
/**
|
||||||
* Checks if the password is valid with the salt
|
* Checks if the password is valid with the salt
|
||||||
*
|
*
|
||||||
* @param {string} salt The salt
|
|
||||||
* @param {string} password The password that the user gave
|
* @param {string} password The password that the user gave
|
||||||
|
* @param {string} hashedPassword The password in the users account
|
||||||
* @return If the password is valid or not
|
* @return If the password is valid or not
|
||||||
*/
|
*/
|
||||||
export function isValidPassword(salt, password) {
|
export function isValidPassword(password, hashedPassword) {
|
||||||
return bcrypt.compareSync(password, salt);
|
return bcrypt.compareSync(password, hashedPassword);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* @param {Number} length
|
* @param {Number} length
|
||||||
* @returns The random string
|
* @returns The random string
|
||||||
*/
|
*/
|
||||||
function randomString(length) {
|
export function randomString(length) {
|
||||||
var result = "";
|
var result = "";
|
||||||
var characters =
|
var characters =
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
@ -1,10 +1,55 @@
|
|||||||
|
import UserModel from "../../models/UserModel";
|
||||||
|
import { connectMongo } from "./mongoHelpers";
|
||||||
|
import { generateSalt, hashPassword } from "./passwordHelpers";
|
||||||
|
import { randomString } from "./stringHelpers";
|
||||||
|
|
||||||
|
connectMongo();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the user with the given email address
|
* Returns the user with the given username
|
||||||
*
|
*
|
||||||
* @param {string} email The users email address
|
* @param {string} username The users username
|
||||||
* @return The users object in mongo or null if not found
|
* @return The user object in mongo or null if not found
|
||||||
*/
|
*/
|
||||||
export async function getUser(email) {
|
export async function getUser(username) {
|
||||||
const user = await UserModel.find({ email: email });
|
return await UserModel.findOne({ username: username });
|
||||||
return user;
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the user with the given upload key
|
||||||
|
*
|
||||||
|
* @param {string} uploadKey The users uploadKey
|
||||||
|
* @return The user object in mongo or null if not found
|
||||||
|
*/
|
||||||
|
export async function getUserByUploadKey(uploadKey) {
|
||||||
|
return await UserModel.findOne({ uploadKey: uploadKey });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new user and returns the user object
|
||||||
|
*
|
||||||
|
* @param {string} username The username of the account
|
||||||
|
* @param {string} password The non hashed password of the account
|
||||||
|
*
|
||||||
|
* @return null if user already exists, true if success, false if fail
|
||||||
|
*/
|
||||||
|
export async function createUser(username, password) {
|
||||||
|
let user = await getUser(username);
|
||||||
|
if (user !== null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const salt = generateSalt();
|
||||||
|
user = await UserModel.create({
|
||||||
|
username: username,
|
||||||
|
password: hashPassword(salt, password),
|
||||||
|
salt: salt,
|
||||||
|
uploadKey: randomString(16),
|
||||||
|
});
|
||||||
|
user.save();
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user