diff --git a/package-lock.json b/package-lock.json index 8881381..69bfbb4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@types/bcrypt": "^5.0.0", + "@types/cookie": "^0.5.1", "@types/node": "20.1.7", "@types/react": "18.2.6", "@types/react-bootstrap": "^0.32.32", @@ -16,6 +17,7 @@ "autoprefixer": "10.4.14", "bcrypt": "^5.1.0", "bootstrap": "^5.3.0", + "cookie": "^0.5.0", "eslint": "8.40.0", "eslint-config-next": "13.4.2", "mysql": "^2.18.1", @@ -529,6 +531,11 @@ "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.5.1.tgz", + "integrity": "sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==" + }, "node_modules/@types/debug": { "version": "4.1.8", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", @@ -1359,6 +1366,14 @@ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", diff --git a/package.json b/package.json index df37877..2ba31b7 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@types/bcrypt": "^5.0.0", + "@types/cookie": "^0.5.1", "@types/node": "20.1.7", "@types/react": "18.2.6", "@types/react-bootstrap": "^0.32.32", @@ -17,6 +18,7 @@ "autoprefixer": "10.4.14", "bcrypt": "^5.1.0", "bootstrap": "^5.3.0", + "cookie": "^0.5.0", "eslint": "8.40.0", "eslint-config-next": "13.4.2", "mysql": "^2.18.1", diff --git a/src/app/[article]/[...slug]/page.tsx b/src/app/[article]/[...slug]/page.tsx index 66a7b96..586ecf8 100644 --- a/src/app/[article]/[...slug]/page.tsx +++ b/src/app/[article]/[...slug]/page.tsx @@ -14,7 +14,9 @@ type Post = { title: String, content: String } + async function getData(slug:String):Promise> { + // Get all posts from the API const res = await fetch(`http://localhost:3000/api/post/${slug}`); // The return value is *not* serialized // You can return Date, Map, Set, etc. diff --git a/src/app/projects/page.tsx b/src/app/projects/page.tsx new file mode 100644 index 0000000..451f4a9 --- /dev/null +++ b/src/app/projects/page.tsx @@ -0,0 +1,29 @@ +import Header from "@/components/header"; +import PageContainer from "@/components/page-container"; +import Navbar from "@/components/navbar"; +import Sidebar from "@/components/sidebar"; +import ArticlePreview from "@/components/news/article-preview" +import ReactDOM from "react"; +import "public/global.css" +import "@/app/index.css" + + +export default function Page() { + return
+
+ + + +

+ Filters +

+
  • filter 1
  • filter 2
  • filter 3
+
+
+ + + +
+
+
; +} \ No newline at end of file diff --git a/src/model/sequelize/Attachment.ts b/src/model/sequelize/Attachment.ts new file mode 100644 index 0000000..32b674a --- /dev/null +++ b/src/model/sequelize/Attachment.ts @@ -0,0 +1,41 @@ +import { Sequelize, DataTypes, Optional, Model, UUIDV4 } from 'sequelize'; +import { MPost } from '@/model/sequelize/Post'; +const sequelize = new Sequelize({ + dialect: 'sqlite', + storage: 'db.sqlite' +}); +interface AttachmentAttributes{ + id: string; + path: string; + post_id: number; +}; +interface AttachmentCreationAttributes extends Optional{}; +class AttachmentModel extends Model{ + id:string = ""; + path:string = ""; + post_id:number|undefined; + createdAt?: Date; + updatedAt?: Date; + // declare title +} +export const MAttachment = sequelize.define( + 'Attachment', + { + id: { + allowNull: false, + autoIncrement: false, + type: DataTypes.UUID, + defaultValue: UUIDV4, + primaryKey: true, + unique: true, + }, + path: { + type: DataTypes.STRING, + }, + post_id: { + type: DataTypes.INTEGER, + key: "Post" + } + } +); +MAttachment.hasOne(MPost); diff --git a/src/model/sequelize/Auth.ts b/src/model/sequelize/Auth.ts index bf6ea1e..ddc02a7 100644 --- a/src/model/sequelize/Auth.ts +++ b/src/model/sequelize/Auth.ts @@ -1,22 +1,37 @@ -import { Sequelize, DataTypes, Optional, Model, UUIDV4 } from 'sequelize'; +// import { Sequelize, DataTypes, Optional, Model, UUIDV4 } from 'sequelize'; +import { + Association, DataTypes, HasManyAddAssociationMixin, HasManyCountAssociationsMixin, + HasManyCreateAssociationMixin, HasManyGetAssociationsMixin, HasManyHasAssociationMixin, + HasManySetAssociationsMixin, HasManyAddAssociationsMixin, HasManyHasAssociationsMixin, + HasManyRemoveAssociationMixin, HasManyRemoveAssociationsMixin, Model, ModelDefined, Optional, + Sequelize, InferAttributes, InferCreationAttributes, CreationOptional, NonAttribute, ForeignKey, BelongsTo, BelongsToGetAssociationMixin, UUIDV4, UUID + } from 'sequelize'; + +import { MUser, UserModel } from '@/model/sequelize/User'; const sequelize = new Sequelize({ dialect: 'sqlite', storage: 'db.sqlite' }); -interface AuthAttributes{ - id: number; - token: string; - user_id: number; -}; -interface AuthCreationAttributes extends Optional,'token'> {}; -class AuthModel extends Model{ - createdAt?: Date; - updatedAt?: Date; - token: any; - // declare title +// interface AuthAttributes{ +// id: number; +// token: string; +// user_id: number; +// }; +// interface AuthCreationAttributes extends Optional,'token'> {}; +// class AuthModel extends Model{ +// createdAt?: Date; +// updatedAt?: Date; +// token: any; +// // declare title +// } +class AuthModel extends Model, InferCreationAttributes> { + declare id: CreationOptional; + declare token: CreationOptional; + declare user_id: number; + declare getUser: BelongsToGetAssociationMixin; + declare user:NonAttribute; } -export const MAuth = sequelize.define( - 'Auth', +export const MAuth = AuthModel.init( { id: { allowNull: false, @@ -34,5 +49,25 @@ export const MAuth = sequelize.define( key: "User" } // date: DataTypes.DATE, + }, + { + tableName: 'Auths', + sequelize // passing the `sequelize` instance is required } -); \ No newline at end of file +); + +MAuth.belongsTo(MUser, { + foreignKey:'user_id', + targetKey:'id', + as: 'user' +}) + +MUser.hasMany(MAuth, { + sourceKey: 'id', + foreignKey: 'user_id', + as: 'authtokens' // this determines the name in `associations`! + }); +// MAuth.belongsTo(MUser, { targetKey: 'id' }); +// MAuth.sync(); + +export { AuthModel } \ No newline at end of file diff --git a/src/model/sequelize/Post.ts b/src/model/sequelize/Post.ts index 69d6f46..d4546c3 100644 --- a/src/model/sequelize/Post.ts +++ b/src/model/sequelize/Post.ts @@ -1,4 +1,5 @@ import { Sequelize, DataTypes, Optional, Model } from 'sequelize'; +import { MAttachment } from '@/model/sequelize/Attachment'; const sequelize = new Sequelize({ dialect: 'sqlite', storage: 'db.sqlite' @@ -29,4 +30,5 @@ export const MPost = sequelize.define( title: DataTypes.STRING, content: DataTypes.STRING, } -); \ No newline at end of file +); +MPost.hasMany(MAttachment); diff --git a/src/model/sequelize/Resource.ts b/src/model/sequelize/Resource.ts new file mode 100644 index 0000000..ffc39da --- /dev/null +++ b/src/model/sequelize/Resource.ts @@ -0,0 +1,73 @@ +import { BelongsToGetAssociationMixin, UUIDV4 } from 'sequelize'; +import { + Association, DataTypes, HasManyAddAssociationMixin, HasManyCountAssociationsMixin, + HasManyCreateAssociationMixin, HasManyGetAssociationsMixin, HasManyHasAssociationMixin, + HasManySetAssociationsMixin, HasManyAddAssociationsMixin, HasManyHasAssociationsMixin, + HasManyRemoveAssociationMixin, HasManyRemoveAssociationsMixin, Model, ModelDefined, Optional, + Sequelize, InferAttributes, InferCreationAttributes, CreationOptional, NonAttribute, ForeignKey, +} from 'sequelize'; +import { UserModel } from '@/model/sequelize/User'; + +const sequelize = new Sequelize({ + dialect: 'sqlite', + storage: 'db.sqlite' +}); +class ResourceModel extends Model, InferCreationAttributes>{ + declare id: CreationOptional; + declare path: string; + declare owner: NonAttribute; + declare getOwner: BelongsToGetAssociationMixin; + + // user_id: number | undefined; + createdAt?: Date; + updatedAt?: Date; + // declare title + declare static associations: { + owner: Association; + }; +} + +ResourceModel.init({ + id: { + allowNull: false, + autoIncrement: false, + type: DataTypes.UUID, + defaultValue: UUIDV4, + primaryKey: true, + unique: true, + }, + path: { + type: DataTypes.STRING + }, + createdAt: { + type: DataTypes.DATE + }, + updatedAt: { + type: DataTypes.DATE + } +},{ + sequelize, + tableName: 'Resource' +}); +// export const MResource = sequelize.define( +// 'Resource', +// { +// id: { +// allowNull: false, +// autoIncrement: false, +// type: DataTypes.UUID, +// defaultValue: UUIDV4, +// primaryKey: true, +// unique: true, +// }, +// path: { +// type: DataTypes.STRING, +// }, +// user_id: { +// type: DataTypes.INTEGER, +// key: "User" +// } +// } +// ); +// UserModel.hasMany(MResource); +// ResourceModel.belongsTo(UserModel); \ No newline at end of file diff --git a/src/model/sequelize/User.ts b/src/model/sequelize/User.ts index 4f949c0..2fc0d05 100644 --- a/src/model/sequelize/User.ts +++ b/src/model/sequelize/User.ts @@ -1,24 +1,40 @@ -import { Sequelize, DataTypes, Optional, Model } from 'sequelize'; +// import { Sequelize, DataTypes, Optional, Model } from 'sequelize'; +import { + Association, DataTypes, HasManyAddAssociationMixin, HasManyCountAssociationsMixin, + HasManyCreateAssociationMixin, HasManyGetAssociationsMixin, HasManyHasAssociationMixin, + HasManySetAssociationsMixin, HasManyAddAssociationsMixin, HasManyHasAssociationsMixin, + HasManyRemoveAssociationMixin, HasManyRemoveAssociationsMixin, Model, ModelDefined, Optional, + Sequelize, InferAttributes, InferCreationAttributes, CreationOptional, NonAttribute, ForeignKey, BelongsTo, BelongsToGetAssociationMixin, UUIDV4, UUID + } from 'sequelize'; + +import { AuthModel } from '@/model/sequelize/Auth'; + const sequelize = new Sequelize({ dialect: 'sqlite', storage: 'db.sqlite' }); -interface UserAttributes{ - id: number; - username: string; - password: string; -}; -interface UserCreationAttributes extends Optional{}; -class UserModel extends Model{ - createdAt?: Date; - updatedAt?: Date; - username: string = ""; - password: string = ""; - id?:number; - // declare title +// interface UserAttributes{ +// id: number; +// username: string; +// password: string; +// }; +// interface UserCreationAttributes extends Optional{}; +// export class UserModel extends Model{ +// createdAt?: Date; +// updatedAt?: Date; +// username: string = ""; +// password: string = ""; +// id?:number; +// // declare title +// } +export class UserModel extends Model, InferCreationAttributes>{ + declare id: number|null; + declare username: string; + declare password: string; + declare getAuths:HasManyGetAssociationsMixin; } -export const MUser = sequelize.define( - 'User', + +export const MUser = UserModel.init( { id: { allowNull: false, @@ -35,5 +51,10 @@ export const MUser = sequelize.define( allowNull: false, type: DataTypes.STRING, } + }, + { + tableName: 'Users', + sequelize // passing the `sequelize` instance is required } -); \ No newline at end of file +); +MUser.sync(); \ No newline at end of file diff --git a/src/pages/api/attachment/index.ts b/src/pages/api/attachment/index.ts new file mode 100644 index 0000000..8249cb6 --- /dev/null +++ b/src/pages/api/attachment/index.ts @@ -0,0 +1,56 @@ +import mysql2, { Connection, RowDataPacket, OkPacket, QueryError } from "mysql2"; +import { getConnection } from "@/db"; +import { Post, postPlaceholder } from "@/model/Models"; +import { getPosts, IPost } from "@/controller/Post"; +import { NextApiRequest, NextApiResponse } from "next"; +import { MPost } from "@/model/sequelize/Post"; +import { MAttachment } from "@/model/sequelize/Attachment"; + + + +import { validatePassword, hashPassword } from "@/util/Auth"; + + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method === 'Post') { + let getAuth = () => { + try { + if (req.headers.authorization === undefined) { + throw "Basic Auth is required"; + } + const authString = Buffer.from(req.headers.authorization.split(" ")[1], "base64").toString("utf8"); + return authString.split(":"); + } catch (error) { + res.status(500).json(error); + return; + } + }; + const auth = getAuth() || ["", ""]; + console.log(auth); + const username = auth[0]; + const password = auth[1]; + // console.log(req.body); + await MUser.sync(); + await MAuth.sync(); + + let user = await MUser.findOne({ where: { username: username } }); + if (user == undefined) { + res.status(401).json("User does not exist"); + return; + } + + if (!(await validatePassword(password, user.password))) { + res.status(401).json("Invalid password"); + return; + } + + let authtoken = await MAuth.findOne({ where: { user_id: user.id } }); + if (authtoken == undefined) { + if (user.id != undefined) { + authtoken = await MAuth.create({ user_id: user.id }); + } + } + + res.status(200).json(authtoken); + } +} \ No newline at end of file diff --git a/src/pages/api/auth/index.ts b/src/pages/api/auth/index.ts index c0f71b6..34040cf 100644 --- a/src/pages/api/auth/index.ts +++ b/src/pages/api/auth/index.ts @@ -1,57 +1,99 @@ -import mysql2, { Connection, RowDataPacket, OkPacket, QueryError } from "mysql2"; +// import mysql2, { Connection, RowDataPacket, OkPacket, QueryError } from "mysql2"; import { getConnection } from "@/db"; -import { Post, postPlaceholder } from "@/model/Models"; +// import { Post, postPlaceholder } from "@/model/Models"; import { getPosts, IPost } from "@/controller/Post"; import { NextApiRequest, NextApiResponse } from "next"; import { MPost } from "@/model/sequelize/Post"; -import { MUser } from "@/model/sequelize/User"; +import { MUser, UserModel } from "@/model/sequelize/User"; import { MAuth } from "@/model/sequelize/Auth"; - +import { cookies } from 'next/headers'; +import { setCookie } from '@/util/Cookies'; import { DataType, Model, Sequelize, UUID } from "sequelize"; import { validatePassword, hashPassword } from "@/util/Auth"; +function getAuth(req: NextApiRequest) { + if (req.headers.authorization === undefined) { + throw "Basic Auth is required"; + } + const authString = Buffer.from(req.headers.authorization.split(" ")[1], "base64").toString("utf8"); + return authString.split(":"); + +}; + +type UserCredentials = { + user: UserModel | undefined, + valid: boolean +} + +async function verifyUserCredentials(req: NextApiRequest, res: NextApiResponse): Promise { + // let user = await MUser.findOne({ where: { username: username } }); + const auth = getAuth(req) || ["", ""]; + console.log(auth); + let credentials: UserCredentials = { user: undefined, valid: false } + const username = auth[0]; + const password = auth[1]; + // console.log(req.body); + await MUser.sync(); + await MAuth.sync(); + + let user = await MUser.findOne({ where: { username: username } }); + if (user == undefined) { + res.status(401).json("User does not exist"); + return; + } + + if (!(await validatePassword(password, user.password))) { + res.status(401).json("Invalid password"); + return; + } + + credentials.valid = true; + credentials.user = user; + return credentials; +} + +async function GET(req: NextApiRequest, res: NextApiResponse) { + let a = req.cookies; + console.log(a); + let credentials = { userid: req.query.userid, token: req.query.token }; + let authtoken = await MAuth.findOne({ + where: { + token: credentials.token, + user_id: credentials.userid + } + }); + if(authtoken){ + // res.setHeader("cookie") + setCookie(res, 'auth', authtoken, { path: '/', maxAge: 2592000 }); + res.status(200).end(); + } + else{ + res.status(401).end(); + } + // console.log(a); + // res.status(200).json(authtokens); +} +async function POST(req: NextApiRequest, res: NextApiResponse) { + + const credentials = await verifyUserCredentials(req, res); + if (!credentials || !credentials.valid || !credentials.user) + return; + const user = await MUser.findOne({ where: { username: credentials.user.username } }); + if (!user) + return; + + res.status(200).json(await MAuth.create({ user_id: user.id })); +} export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.method === 'GET') { - let getAuth = () => { - try { - if (req.headers.authorization === undefined) { - throw "Basic Auth is required"; - } - const authString = Buffer.from(req.headers.authorization.split(" ")[1], "base64").toString("utf8"); - return authString.split(":"); - } catch (error) { - res.status(500).json(error); - return; - } - }; - const auth = getAuth() || ["", ""]; - console.log(auth); - const username = auth[0]; - const password = auth[1]; - // console.log(req.body); - await MUser.sync(); - await MAuth.sync(); - - let user = await MUser.findOne({ where: { username: username } }); - if (user == undefined) { - res.status(401).json("User does not exist"); - return; - } - - if (!(await validatePassword(password, user.password))) { - res.status(401).json("Invalid password"); - return; - } - - let authtoken = await MAuth.findOne({ where: { user_id: user.id } }); - if (authtoken == undefined) { - if (user.id != undefined) { - authtoken = await MAuth.create({ user_id: user.id }); - } - } - - res.status(200).json(authtoken); + switch (req.method){ + case 'GET': + GET(req, res); break; + case 'POST': + POST(req, res); break; + default: + res.status(404).end(); + break; } } \ No newline at end of file diff --git a/src/pages/api/user/index.ts b/src/pages/api/user/index.ts index 41b4883..96007ba 100644 --- a/src/pages/api/user/index.ts +++ b/src/pages/api/user/index.ts @@ -1,42 +1,55 @@ -import mysql2, { Connection, RowDataPacket, OkPacket, QueryError } from "mysql2"; -import { getConnection } from "@/db"; +// import { getConnection } from "@/db"; import { Post, postPlaceholder } from "@/model/Models"; import { getPosts, IPost } from "@/controller/Post"; import { NextApiRequest, NextApiResponse } from "next"; // import { MPost, MUser, MAuth } from "@/model/Models" -import { MPost } from "@/model/sequelize/Post"; +// import { MPost } from "@/model/sequelize/Post"; +// import { MAuth } from "@/model/sequelize/Auth"; import { MUser } from "@/model/sequelize/User"; -import { MAuth } from "@/model/sequelize/Auth"; import { DataType, Model, Sequelize, UUID } from "sequelize"; import { validatePassword, hashPassword } from "@/util/Auth"; +async function POST(req: NextApiRequest, res: NextApiResponse){ + MUser.sync(); + const username = req.body.username; + const password = req.body.password; + await MUser.sync() + var user = await MUser.findOne({ where: { username: username } }); + if (user != undefined){ + res.status(500).json("User with that username already exists"); + return; + } + const hash = await hashPassword(password) + user = await MUser.create({ + username: username, + password: hash + }) + res.status(200).json(user); + return; + + +} + +async function GET(req: NextApiRequest, res: NextApiResponse){ + MUser.sync(); + const username = req.body.username; + const password = req.body.password; + await MUser.sync() + var users = await MUser.findAll(); + res.status(200).json(users); + return; +} + + + export default async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method === 'POST') { - const username = req.body.username; - const password = req.body.password; - await MUser.sync() - var user = await MUser.findOne({ where: { username: username } }); - if (user != undefined){ - res.status(500).json("User with that username already exists"); - return; - } - const hash = await hashPassword(password) - user = await MUser.create({ - username: username, - password: hash - }) - res.status(200).json(user); - return; + POST(req,res); } - if (req.method === 'GET') { - const username = req.body.username; - const password = req.body.password; - await MUser.sync() - var users = await MUser.findAll(); - res.status(200).json(users); - return; + else if(req.method === 'GET'){ + GET(req,res); } } \ No newline at end of file diff --git a/src/util/Cookies.ts b/src/util/Cookies.ts new file mode 100644 index 0000000..143de78 --- /dev/null +++ b/src/util/Cookies.ts @@ -0,0 +1,21 @@ +import { serialize, CookieSerializeOptions } from 'cookie'; +import { NextApiResponse } from 'next' + +/** + * This sets `cookie` using the `res` object + */ +export const setCookie = ( + res: NextApiResponse, + name: string, + value: unknown, + options: CookieSerializeOptions = {} +) => { + const stringValue = + typeof value === 'object' ? 'j:' + JSON.stringify(value) : String(value) + + if (typeof options.maxAge === 'number') { + options.expires = new Date(Date.now() + options.maxAge * 1000) + } + + res.setHeader('Set-Cookie', serialize(name, stringValue, options)) +} \ No newline at end of file