diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..f7e7759 --- /dev/null +++ b/backend/README.md @@ -0,0 +1,6 @@ +# Worktime Backend + +To start dev server: +``` +$ denon dev +``` diff --git a/backend/src/authorized.ts b/backend/src/authorized.ts new file mode 100644 index 0000000..a5f941d --- /dev/null +++ b/backend/src/authorized.ts @@ -0,0 +1,7 @@ +export default async (ctx: any, next: Function) => { + const userId = await ctx.state.session.get('userId'); + if (!userId) { + ctx.throw(403); + } + await next(); +} diff --git a/backend/src/index.ts b/backend/src/index.ts index 2ef1583..442df72 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -3,7 +3,7 @@ import { Application } from "https://deno.land/x/oak@v10.5.1/mod.ts"; import { Session, CookieStore } from "https://deno.land/x/oak_sessions@v3.4.0/mod.ts"; import { oakCors } from "https://deno.land/x/cors@v1.2.2/mod.ts"; -import routes from './routes/index.ts' +import routes from './routes.ts' const PORT = Number(Deno.env.get('PORT')); const SECRET = Deno.env.get('SECRET'); diff --git a/backend/src/models/Error.ts b/backend/src/models/Error.ts new file mode 100644 index 0000000..0d535f0 --- /dev/null +++ b/backend/src/models/Error.ts @@ -0,0 +1,9 @@ +export default class Error { + msg: string; + code: string; + + constructor(msg: string, type: string, code: number) { + this.msg = msg; + this.code = `${type}-${code}`; + } +} diff --git a/backend/src/models/User.ts b/backend/src/models/User.ts index c6c4333..e7b2463 100644 --- a/backend/src/models/User.ts +++ b/backend/src/models/User.ts @@ -10,17 +10,29 @@ export default class User { constructor({ providerId, - authProvider + authProvider, + tasks, + categories, + updatedAt }: { providerId: string, - authProvider: string + authProvider: string, + tasks?: Array, + categories?: Array, + updatedAt?: Date | string }) { this.providerId = providerId; this.authProvider = authProvider; this.id = `${providerId}@${authProvider}`; - this.tasks = []; - this.categories = []; - this.updatedAt = new Date(); + this.tasks = tasks ?? []; + this.categories = categories ?? []; + if (updatedAt instanceof Date) { + this.updatedAt = updatedAt; + } else if (updatedAt) { + this.updatedAt = new Date(updatedAt); + } else { + this.updatedAt = new Date(); + } } addTask(task: Task) { diff --git a/backend/src/routes/index.ts b/backend/src/routes.ts similarity index 51% rename from backend/src/routes/index.ts rename to backend/src/routes.ts index c440514..233f88f 100644 --- a/backend/src/routes/index.ts +++ b/backend/src/routes.ts @@ -2,8 +2,9 @@ import "https://deno.land/x/dotenv@v3.2.0/load.ts"; import { Router } from "https://deno.land/x/oak@v10.5.1/mod.ts"; import { MongoClient } from "https://deno.land/x/mongo@v0.29.4/mod.ts"; -import User from '../models/User.ts'; -import getProviderId from '../getProviderId.ts'; +import User from './models/User.ts'; +import authorized from './authorized.ts'; +import getProviderId from './getProviderId.ts'; const MONGODB_URI = String(Deno.env.get('MONGODB_URI')); @@ -16,10 +17,21 @@ const users = db.collection("users"); const endpoints = new Router() .post("/login", async (ctx) => { const body = await ctx.request.body({ type: "json" }).value; + const userId = await ctx.state.session.get('userId') const token = body.token; const authProvider = body.authProvider; + if (userId) { + ctx.response.body = 'success'; + return + } + const providerId = await getProviderId(token, authProvider); + if (!providerId) { + ctx.response.status = 500 + ctx.response.body = 'error'; + return + } let user = await users.findOne({ providerId, @@ -38,6 +50,26 @@ const endpoints = new Router() ctx.response.body = 'success'; }) + .post('/sync', authorized, async (ctx) => { + const body = await ctx.request.body({ type: "json" }).value; + const userId = await ctx.state.session.get('userId') + const clientSideUser = new User(body.user); + + const serverSideUser = await users.findOne({ id: userId }) + if (!serverSideUser) { + ctx.response.status = 500 + ctx.response.body = 'error'; + return + } + + if (serverSideUser.updatedAt <= clientSideUser.updatedAt) { + serverSideUser.tasks = clientSideUser.tasks; + serverSideUser.categories = clientSideUser.categories; + serverSideUser.updatedAt = new Date(); + } + + ctx.response.body = serverSideUser; + }) const routes = new Router() .use("/api", endpoints.routes(), endpoints.allowedMethods()) diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 3938a03..e5d9bc2 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -4,6 +4,16 @@
+ +