Compare commits
5 Commits
4c9765b4a3
...
main
Author | SHA1 | Date | |
---|---|---|---|
92960c4fa7 | |||
f8559b477b | |||
cb529e47d9 | |||
dabd5ea0f0 | |||
5c769bfa2f |
695
api/package-lock.json
generated
695
api/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,7 @@
|
|||||||
"format": "npx prettier --write ."
|
"format": "npx prettier --write ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"bcrypt": "^5.1.1",
|
||||||
"colors": "1.4.0",
|
"colors": "1.4.0",
|
||||||
"cookie-parser": "^1.4.5",
|
"cookie-parser": "^1.4.5",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
@@ -33,6 +34,7 @@
|
|||||||
"sequelize": "^6.15.0"
|
"sequelize": "^6.15.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/bcrypt": "^5.0.2",
|
||||||
"@types/cookie-parser": "^1.4.2",
|
"@types/cookie-parser": "^1.4.2",
|
||||||
"@types/cors": "^2.8.10",
|
"@types/cors": "^2.8.10",
|
||||||
"@types/ejs": "^3.0.6",
|
"@types/ejs": "^3.0.6",
|
||||||
@@ -46,6 +48,7 @@
|
|||||||
"@types/shelljs": "^0.8.9",
|
"@types/shelljs": "^0.8.9",
|
||||||
"jest": "^27.0.6",
|
"jest": "^27.0.6",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
|
"nodemon": "^3.0.2",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"shelljs": "^0.8.4",
|
"shelljs": "^0.8.4",
|
||||||
|
@@ -1,13 +1,21 @@
|
|||||||
import env from './environment'
|
import env from './environment'
|
||||||
import { Err, Succ } from '../services/globalService'
|
import { Err, Succ } from '../services/globalService'
|
||||||
import db from './sequelize.config'
|
import db from './sequelize.config'
|
||||||
|
import Client from '../models/Client'
|
||||||
|
import Project from '../models/Project'
|
||||||
|
import Task from '../models/Task'
|
||||||
|
import User from '../models/User'
|
||||||
|
|
||||||
function connect() {
|
async function connect() {
|
||||||
if (!env.NORK.database) {
|
if (!env.NORK.database) {
|
||||||
new Err(500, 'no database is in norkcfg.json')
|
new Err(500, 'no database is in norkcfg.json')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (env.NORK.database.orm == 'sequelize') {
|
if (env.NORK.database.orm == 'sequelize') {
|
||||||
|
await User.sync()
|
||||||
|
await Client.sync()
|
||||||
|
await Project.sync()
|
||||||
|
await Task.sync({ alter: true })
|
||||||
db.sync()
|
db.sync()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
new Succ(200, 'connected to db')
|
new Succ(200, 'connected to db')
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { Request, Response } from 'express'
|
import { Request, Response } from 'express'
|
||||||
import Task from '../models/Task'
|
import Task from '../models/Task'
|
||||||
|
import { Err, Succ } from '../services/globalService'
|
||||||
|
|
||||||
export async function get(req: Request, res: Response) {
|
export async function get(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
@@ -11,9 +12,34 @@ export async function get(req: Request, res: Response) {
|
|||||||
}
|
}
|
||||||
export async function add(req: Request, res: Response) {
|
export async function add(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const task = await Task.create(req.body)
|
const payload = req.body
|
||||||
|
payload.author_id = res.locals.user._id
|
||||||
|
console.log(payload)
|
||||||
|
const task = await Task.create(payload)
|
||||||
res.json(task)
|
res.json(task)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).send(error)
|
res.status(500).send(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function edit(req: Request, res: Response) {
|
||||||
|
try {
|
||||||
|
const payload = req.body
|
||||||
|
payload.author_id = res.locals.user._id
|
||||||
|
const task: any = await Task.findByPk(payload._id)
|
||||||
|
|
||||||
|
if (!task) {
|
||||||
|
return res.status(400).json(new Err(400, 'Task not found'))
|
||||||
|
}
|
||||||
|
|
||||||
|
task.title = payload.title
|
||||||
|
task.timeEnd = payload.timeEnd
|
||||||
|
task.timeStart = payload.timeStart
|
||||||
|
|
||||||
|
await task.save()
|
||||||
|
|
||||||
|
return res.json(new Succ(200, 'Task updated succesfully'))
|
||||||
|
} catch (error) {
|
||||||
|
return res.status(500).json(new Err(400, 'something went wrong', error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
76
api/src/controllers/userController.ts
Normal file
76
api/src/controllers/userController.ts
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import { Request, Response } from 'express'
|
||||||
|
import bcrypt from 'bcrypt'
|
||||||
|
import jwt from 'jsonwebtoken'
|
||||||
|
import env from '../config/environment'
|
||||||
|
import User from '../models/User'
|
||||||
|
import { Err, Succ } from '../services/globalService'
|
||||||
|
|
||||||
|
export async function login(req: Request, res: Response) {}
|
||||||
|
|
||||||
|
export async function signup(req: Request, res: Response) {
|
||||||
|
try {
|
||||||
|
const payload = req.body
|
||||||
|
|
||||||
|
payload.password = await bcrypt.hash(payload.password, 12)
|
||||||
|
const user = await User.create(payload)
|
||||||
|
|
||||||
|
res.status(201).json(new Succ(201, 'user was successfully signed up'))
|
||||||
|
} catch (err: any) {
|
||||||
|
new Err(500, err)
|
||||||
|
res.status(500).json(new Err(500, 'something went wrong'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function signin(req: Request, res: Response) {
|
||||||
|
try {
|
||||||
|
const payload = req.body
|
||||||
|
|
||||||
|
const user: any = await User.findOne({ where: { email: payload.email } })
|
||||||
|
if (!user) {
|
||||||
|
res.cookie('jwt', '', { httpOnly: true, maxAge: 0 })
|
||||||
|
res.cookie('auth', false, { httpOnly: false, maxAge: 0 })
|
||||||
|
res.status(401).json(new Err(401, 'email or password is wrong'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await bcrypt.compare(payload.password, user.password)) {
|
||||||
|
const maxAge = 3 * 24 * 60 * 60 // 3 days in seconds
|
||||||
|
const createToken = (id: any) => {
|
||||||
|
return jwt.sign({ id }, env.JWT_SECRET, {
|
||||||
|
expiresIn: maxAge
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = createToken(user._id)
|
||||||
|
res.cookie('jwt', token, { httpOnly: true, maxAge: maxAge * 1000 })
|
||||||
|
res.cookie('auth', true, { httpOnly: false, maxAge: maxAge * 1000 })
|
||||||
|
|
||||||
|
res.json(new Succ(200, 'user is logged in'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res.cookie('jwt', '', { httpOnly: true, maxAge: 0 })
|
||||||
|
res.cookie('auth', false, { httpOnly: false, maxAge: 0 })
|
||||||
|
res.status(401).json(new Err(401, 'email or password is wrong'))
|
||||||
|
} catch (err: any) {
|
||||||
|
new Err(500, err)
|
||||||
|
res.status(500).json(new Err(500, 'something went wrong'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function logout(req: Request, res: Response) {
|
||||||
|
res.cookie('jwt', '', { httpOnly: true, maxAge: 0 })
|
||||||
|
res.cookie('auth', false, { httpOnly: false, maxAge: 0 })
|
||||||
|
res.json(new Succ(200, 'user was logged out'))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function status(req: Request, res: Response) {
|
||||||
|
try {
|
||||||
|
let userObject = res.locals.user
|
||||||
|
userObject.password = undefined
|
||||||
|
userObject.__v = undefined
|
||||||
|
res.status(200).json(new Succ(200, 'user is logged in', userObject))
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json(new Err(500, 'somehting went wrong', error))
|
||||||
|
}
|
||||||
|
}
|
@@ -6,7 +6,7 @@ import User from '../models/User' // uncomment this
|
|||||||
|
|
||||||
export function requireAuth(req: Request, res: Response, next: NextFunction) {
|
export function requireAuth(req: Request, res: Response, next: NextFunction) {
|
||||||
const token = req.cookies.jwt
|
const token = req.cookies.jwt
|
||||||
new Err(500, 'uncomment code in authMiddleware before using!')
|
//new Err(500, 'uncomment code in authMiddleware before using!')
|
||||||
if (token) {
|
if (token) {
|
||||||
jwt.verify(token, env.JWT_SECRET, async (err: any, decodedToken: any) => {
|
jwt.verify(token, env.JWT_SECRET, async (err: any, decodedToken: any) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -14,25 +14,16 @@ export function requireAuth(req: Request, res: Response, next: NextFunction) {
|
|||||||
res.status(401).json(new Err(401, 'user is not authenticated'))
|
res.status(401).json(new Err(401, 'user is not authenticated'))
|
||||||
}
|
}
|
||||||
if (!err) {
|
if (!err) {
|
||||||
const user = (async () => {
|
const user = await User.findByPk(decodedToken.id)
|
||||||
if (env.NORK.db.orm) {
|
console.log('TADY', user)
|
||||||
if (env.NORK.db.orm == 'sequelize') {
|
|
||||||
return await User.findByPk(decodedToken.id)
|
|
||||||
}
|
|
||||||
if (env.NORK.db.orm == 'mongoose') {
|
|
||||||
return await User.findById(decodedToken.id)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
|
|
||||||
if (user === null) {
|
if (user === null) {
|
||||||
|
console.log('1')
|
||||||
res.status(401).json(new Err(401, 'user is not authenticated'))
|
res.status(401).json(new Err(401, 'user is not authenticated'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res.locals.user = user
|
res.locals.user = user
|
||||||
|
console.log('2')
|
||||||
new Succ(100, 'user is authenticated')
|
new Succ(100, 'user is authenticated')
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
@@ -40,6 +31,7 @@ export function requireAuth(req: Request, res: Response, next: NextFunction) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
|
console.log('3')
|
||||||
res.status(401).json(new Err(401, 'user is not authenticated'))
|
res.status(401).json(new Err(401, 'user is not authenticated'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
44
api/src/models/Client.ts
Normal file
44
api/src/models/Client.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { DataTypes, Model } from 'sequelize'
|
||||||
|
import path from 'path'
|
||||||
|
import db from '../config/sequelize.config'
|
||||||
|
import User from './User'
|
||||||
|
|
||||||
|
class Instance extends Model {}
|
||||||
|
|
||||||
|
Instance.init(
|
||||||
|
{
|
||||||
|
_id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
allowNull: false,
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
hourlyRate: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
contact: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
author_id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
allowNull: false,
|
||||||
|
references: {
|
||||||
|
model: User,
|
||||||
|
key: '_id'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sequelize: db,
|
||||||
|
tableName: path.basename(__filename).split('.')[0].toLowerCase()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Instance
|
49
api/src/models/Project.ts
Normal file
49
api/src/models/Project.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { DataTypes, Model } from 'sequelize'
|
||||||
|
import path from 'path'
|
||||||
|
import db from '../config/sequelize.config'
|
||||||
|
import User from './User'
|
||||||
|
import Client from './Client'
|
||||||
|
|
||||||
|
class Instance extends Model {}
|
||||||
|
|
||||||
|
Instance.init(
|
||||||
|
{
|
||||||
|
_id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
allowNull: false,
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
hourlyRate: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
client_id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
allowNull: false,
|
||||||
|
references: {
|
||||||
|
model: Client,
|
||||||
|
key: '_id'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
author_id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
allowNull: false,
|
||||||
|
references: {
|
||||||
|
model: User,
|
||||||
|
key: '_id'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sequelize: db,
|
||||||
|
tableName: path.basename(__filename).split('.')[0].toLowerCase()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Instance
|
@@ -1,6 +1,9 @@
|
|||||||
import { DataTypes, Model } from 'sequelize'
|
import { DataTypes, Model } from 'sequelize'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import db from '../config/sequelize.config'
|
import db from '../config/sequelize.config'
|
||||||
|
import User from './User'
|
||||||
|
import Client from './Client'
|
||||||
|
import Project from './Project'
|
||||||
|
|
||||||
class Instance extends Model {}
|
class Instance extends Model {}
|
||||||
|
|
||||||
@@ -14,14 +17,6 @@ Instance.init(
|
|||||||
unique: true
|
unique: true
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
type: DataTypes.STRING,
|
|
||||||
allowNull: false
|
|
||||||
},
|
|
||||||
client: {
|
|
||||||
type: DataTypes.STRING,
|
|
||||||
allowNull: true
|
|
||||||
},
|
|
||||||
project: {
|
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: true
|
allowNull: true
|
||||||
},
|
},
|
||||||
@@ -32,6 +27,22 @@ Instance.init(
|
|||||||
timeEnd: {
|
timeEnd: {
|
||||||
type: DataTypes.BIGINT,
|
type: DataTypes.BIGINT,
|
||||||
allowNull: true
|
allowNull: true
|
||||||
|
},
|
||||||
|
project_id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
allowNull: true,
|
||||||
|
references: {
|
||||||
|
model: Project,
|
||||||
|
key: '_id'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
author_id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
allowNull: false,
|
||||||
|
references: {
|
||||||
|
model: User,
|
||||||
|
key: '_id'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -8,28 +8,29 @@ Instance.init(
|
|||||||
{
|
{
|
||||||
_id: {
|
_id: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
unique: true,
|
unique: true
|
||||||
},
|
},
|
||||||
username: {
|
username: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: false,
|
allowNull: false
|
||||||
},
|
},
|
||||||
password: {
|
password: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: false,
|
allowNull: false
|
||||||
},
|
},
|
||||||
email: {
|
email: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
unique: true,
|
unique: true
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sequelize: db,
|
sequelize: db,
|
||||||
tableName: path.basename(__filename).split('.')[0].toLowerCase(),
|
tableName: path.basename(__filename).split('.')[0].toLowerCase()
|
||||||
},
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
export default Instance
|
export default Instance
|
||||||
|
@@ -1,8 +1,16 @@
|
|||||||
import { Router } from 'express'
|
import { Router } from 'express'
|
||||||
import * as taskController from "../controllers/taskController"
|
import * as taskController from '../controllers/taskController'
|
||||||
|
import * as userController from '../controllers/userController'
|
||||||
|
import { requireAuth } from '../middlewares/authMiddleware'
|
||||||
|
|
||||||
export const router = Router()
|
export const router = Router()
|
||||||
//const mws = [handleValidation.handleValidationError]
|
//const mws = [handleValidation.handleValidationError]
|
||||||
|
|
||||||
router.get("/task/get", taskController.get)
|
router.get('/task/get', requireAuth, taskController.get)
|
||||||
router.post("/task/add", taskController.add)
|
router.post('/task/add', requireAuth, taskController.add)
|
||||||
|
router.post('/task/edit', requireAuth, taskController.edit)
|
||||||
|
|
||||||
|
router.post('/auth/signup', userController.signup)
|
||||||
|
router.post('/auth/signin', userController.signin)
|
||||||
|
router.post('/auth/logout', requireAuth, userController.logout)
|
||||||
|
router.get('/auth/status', requireAuth, userController.status)
|
||||||
|
47
frontend/src/api/user.js
Normal file
47
frontend/src/api/user.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
const api_url = 'http://localhost:6060'
|
||||||
|
const userAPIS = {
|
||||||
|
async getUserStatus() {
|
||||||
|
const res = await fetch(`${api_url}/api/v1/auth/status`, {
|
||||||
|
credentials: 'include'
|
||||||
|
})
|
||||||
|
return res.json()
|
||||||
|
},
|
||||||
|
|
||||||
|
async loginUser(formState) {
|
||||||
|
const res = await fetch(`${api_url}/api/v1/auth/signin`, {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(formState)
|
||||||
|
})
|
||||||
|
return await res.json()
|
||||||
|
},
|
||||||
|
|
||||||
|
async logoutUser() {
|
||||||
|
const res = await fetch(`${api_url}/api/v1/auth/logout`, {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({})
|
||||||
|
})
|
||||||
|
return await res.json()
|
||||||
|
},
|
||||||
|
|
||||||
|
async registerUser(formState) {
|
||||||
|
const res = await fetch(`${api_url}/api/v1/auth/signup`, {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(formState)
|
||||||
|
})
|
||||||
|
return res.json()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default userAPIS
|
37
frontend/src/components/FormBase.vue
Normal file
37
frontend/src/components/FormBase.vue
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<script setup>
|
||||||
|
defineProps(['btn-txt', 'api-endpoint'])
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<form>
|
||||||
|
<slot></slot>
|
||||||
|
<button @click="formAction">{{ btnTxt }}</button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import AppStore from '@/stores/AppStore'
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
d: {
|
||||||
|
email: undefined,
|
||||||
|
password: undefined,
|
||||||
|
username: undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
formAction() {
|
||||||
|
console.log(this.d)
|
||||||
|
//AppStore.sendAdd(null, this.$props.apiEndpoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -30,37 +30,78 @@ export default {
|
|||||||
timer: undefined,
|
timer: undefined,
|
||||||
timerState: 'Start',
|
timerState: 'Start',
|
||||||
task: {
|
task: {
|
||||||
startTime: 0,
|
timeStart: 0,
|
||||||
stopTime: 0,
|
timeEnd: 0,
|
||||||
title: ''
|
title: ''
|
||||||
}
|
},
|
||||||
|
restore: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
formattedElapsedTime() {
|
formattedElapsedTime() {
|
||||||
const date = new Date(this.timeNow - this.task.startTime)
|
const date = new Date(this.timeNow - this.task.timeStart)
|
||||||
const utc = date.toUTCString()
|
const utc = date.toUTCString()
|
||||||
return utc.substr(utc.indexOf(':') - 2, 8)
|
return utc.substr(utc.indexOf(':') - 2, 8)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
startStopTimer() {
|
async startStopTimer() {
|
||||||
if (this.timerState == 'Start') {
|
if (this.timerState == 'Start') {
|
||||||
this.timerState = 'Stop'
|
this.timerState = 'Stop'
|
||||||
this.task.startTime = Date.now()
|
console.log('TADY', this.restore)
|
||||||
|
if (this.restore == false) {
|
||||||
|
console.log('TADY')
|
||||||
|
this.task.timeStart = Date.now()
|
||||||
|
}
|
||||||
|
this.timeNow = Date.now()
|
||||||
console.log('timer started')
|
console.log('timer started')
|
||||||
this.timer = setInterval(() => {
|
this.timer = setInterval(() => {
|
||||||
this.timeNow = Date.now()
|
this.timeNow = Date.now()
|
||||||
}, 1000)
|
}, 1000)
|
||||||
|
if (!this.task._id) {
|
||||||
|
AppStore.data.newTask = {
|
||||||
|
title: this.task.title,
|
||||||
|
timeStart: this.task.timeStart
|
||||||
|
}
|
||||||
|
await AppStore.sendAdd(AppStore.data.newTask, '/task/add')
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
this.task.timeEnd = Date.now()
|
||||||
this.timerState = 'Start'
|
this.timerState = 'Start'
|
||||||
this.task.stopTime = Date.now()
|
|
||||||
clearInterval(this.timer)
|
clearInterval(this.timer)
|
||||||
this.timer = undefined
|
this.timer = undefined
|
||||||
AppStore.data.newTask = this.task
|
this.timeNow = 0
|
||||||
console.log(AppStore.data.newTask)
|
|
||||||
|
AppStore.data.newTask = {
|
||||||
|
title: this.task.title,
|
||||||
|
timeStart: this.task.timeStart,
|
||||||
|
timeEnd: this.task.timeEnd
|
||||||
|
}
|
||||||
|
if (this.task._id) {
|
||||||
|
AppStore.data.newTask._id = this.task._id
|
||||||
|
await AppStore.sendAdd(AppStore.data.newTask, '/task/edit')
|
||||||
|
} else {
|
||||||
|
await AppStore.sendAdd(AppStore.data.newTask, '/task/add')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.task = {
|
||||||
|
timeStart: 0,
|
||||||
|
timeEnd: 0,
|
||||||
|
title: ''
|
||||||
|
}
|
||||||
|
this.restore = false
|
||||||
|
this.$emit('get-tasks')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
await AppStore.fetchData()
|
||||||
|
const task = AppStore.data.fetchedTasks.filter((task) => task.timeEnd === null)
|
||||||
|
if (task.length > 0) {
|
||||||
|
this.task = task[0]
|
||||||
|
this.restore = true
|
||||||
|
this.startStopTimer()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@@ -2,6 +2,14 @@ import { createRouter, createWebHistory } from 'vue-router'
|
|||||||
import TrackerView from '../views/TrackerView.vue'
|
import TrackerView from '../views/TrackerView.vue'
|
||||||
import SignupView from '../views/SignupView.vue'
|
import SignupView from '../views/SignupView.vue'
|
||||||
import LoginView from '../views/LoginView.vue'
|
import LoginView from '../views/LoginView.vue'
|
||||||
|
import userAPIS from '../api/user'
|
||||||
|
|
||||||
|
const authGuard = async () => {
|
||||||
|
const data = await userAPIS.getUserStatus()
|
||||||
|
if (!data.data) {
|
||||||
|
throw router.push('/')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
@@ -10,7 +18,7 @@ const router = createRouter({
|
|||||||
path: '/',
|
path: '/',
|
||||||
name: 'home',
|
name: 'home',
|
||||||
//component: HomeView
|
//component: HomeView
|
||||||
redirect: '/tracker'
|
redirect: '/tracker'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/login',
|
path: '/login',
|
||||||
@@ -26,7 +34,10 @@ const router = createRouter({
|
|||||||
path: '/tracker',
|
path: '/tracker',
|
||||||
name: 'tracker',
|
name: 'tracker',
|
||||||
component: TrackerView,
|
component: TrackerView,
|
||||||
alias: '/'
|
alias: '/',
|
||||||
|
meta: {
|
||||||
|
requiresAuth: true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/about',
|
path: '/about',
|
||||||
@@ -39,4 +50,20 @@ const router = createRouter({
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
router.beforeEach(async (to, from, next) => {
|
||||||
|
const requiresAuth = to.matched.some((x) => x.meta.requiresAuth)
|
||||||
|
const requiresGuest = to.matched.some((x) => x.meta.requiresGuest)
|
||||||
|
|
||||||
|
if (requiresAuth) {
|
||||||
|
const userStatus = await userAPIS.getUserStatus()
|
||||||
|
if (!userStatus.data) {
|
||||||
|
router.replace('/login')
|
||||||
|
next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
@@ -20,7 +20,10 @@ export default {
|
|||||||
},
|
},
|
||||||
async fetchData() {
|
async fetchData() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(this.api_url + '/task/get')
|
const response = await fetch(this.api_url + '/task/get', {
|
||||||
|
credentials: 'include'
|
||||||
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error! Status: ${response.status}`)
|
throw new Error(`HTTP error! Status: ${response.status}`)
|
||||||
}
|
}
|
||||||
@@ -31,5 +34,22 @@ export default {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching data:', error)
|
console.error('Error fetching data:', error)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
async sendAdd(data, url) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(this.api_url + url, {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
})
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! Status: ${response.status}`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error sending data:', error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,15 +1,33 @@
|
|||||||
|
<script setup>
|
||||||
|
import router from '@/router'
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<form>
|
<form>
|
||||||
<input type="email" placeholder="Email">
|
<label for="email">Email:</label>
|
||||||
<input type="password" placeholder="Password">
|
<input type="email" id="email" v-model="email" placeholder="Email" />
|
||||||
<button>Log in</button>
|
<label for="password">Password:</label>
|
||||||
</form>
|
<input type="password" id="password" v-model="password" placeholder="Password" />
|
||||||
|
<button @click.prevent="formAction">Log in</button>
|
||||||
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<script>
|
||||||
form {
|
import User from '@/api/user'
|
||||||
display: flex;
|
export default {
|
||||||
flex-direction: column;
|
data() {
|
||||||
align-items: center;
|
return {
|
||||||
|
email: '',
|
||||||
|
password: ''
|
||||||
}
|
}
|
||||||
</style>
|
},
|
||||||
|
methods: {
|
||||||
|
async formAction() {
|
||||||
|
const status = await User.loginUser({ email: this.email, password: this.password })
|
||||||
|
if (status.code == 200) {
|
||||||
|
throw router.push('/')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
@@ -1,3 +1,36 @@
|
|||||||
|
<script setup>
|
||||||
|
import router from '@/router'
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<b>signup</b>
|
<form>
|
||||||
</template>
|
<label for="username">Username:</label>
|
||||||
|
<input type="text" id="username" v-model="username" placeholder="Username" />
|
||||||
|
<label for="email">Email:</label>
|
||||||
|
<input type="email" id="email" v-model="email" placeholder="Email" />
|
||||||
|
<label for="password">Password:</label>
|
||||||
|
<input type="password" id="password" v-model="password" placeholder="Password" />
|
||||||
|
<button @click.prevent="formAction">Sign up</button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import User from '@/api/user'
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
username: '',
|
||||||
|
email: '',
|
||||||
|
password: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async formAction() {
|
||||||
|
const status = await User.registerUser({ username: this.username, email: this.email, password: this.password })
|
||||||
|
if (status.code == 201) {
|
||||||
|
throw router.push('/')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
@@ -6,11 +6,11 @@ import TaskRecord from '@/components/TaskRecord.vue'
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main>
|
<main>
|
||||||
<TrackerTimer />
|
<TrackerTimer @get-tasks="getTasks" />
|
||||||
|
|
||||||
<div class="task-day-wrapper">
|
<div class="task-day-wrapper">
|
||||||
<TaskHeader />
|
<TaskHeader />
|
||||||
<div class="task-wrapper" v-for="task in tasks" :key="task._id">
|
<div class="task-wrapper" v-for="task in sortedData" :key="task._id">
|
||||||
<TaskRecord :task="task" />
|
<TaskRecord :task="task" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -26,6 +26,17 @@ export default {
|
|||||||
tasks: AppStore.data.fetchedTasks
|
tasks: AppStore.data.fetchedTasks
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
async getTasks() {
|
||||||
|
await AppStore.fetchData()
|
||||||
|
this.tasks = AppStore.data.fetchedTasks
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
sortedData() {
|
||||||
|
return this.tasks.slice().sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))
|
||||||
|
}
|
||||||
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
await AppStore.fetchData()
|
await AppStore.fetchData()
|
||||||
this.tasks = AppStore.data.fetchedTasks
|
this.tasks = AppStore.data.fetchedTasks
|
||||||
|
Reference in New Issue
Block a user