Skip to content

Commit

Permalink
Merge pull request #1 from andyruwruw/refactor
Browse files Browse the repository at this point in the history
Refactor
  • Loading branch information
andyruwruw committed Oct 13, 2020
2 parents e9c2ef8 + 8c3d3ae commit 084a8a1
Show file tree
Hide file tree
Showing 63 changed files with 14,858 additions and 1,826 deletions.
3 changes: 3 additions & 0 deletions .browserslistrc
@@ -0,0 +1,3 @@
> 1%
last 2 versions
not dead
17 changes: 17 additions & 0 deletions .eslintrc.js
@@ -0,0 +1,17 @@
module.exports = {
root: true,
env: {
node: true
},
'extends': [
'plugin:vue/essential',
'eslint:recommended'
],
parserOptions: {
parser: 'babel-eslint'
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
}
}
26 changes: 26 additions & 0 deletions .gitignore
@@ -0,0 +1,26 @@
.DS_Store
node_modules
/dist

# local env files
.env.local
.env.*.local

.env

# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

.vercel
21 changes: 0 additions & 21 deletions LICENSE.txt

This file was deleted.

85 changes: 43 additions & 42 deletions README.md
@@ -1,47 +1,48 @@
# Countdown-Timer
# countdown timer

Generates timers based on the closest or current event on your google calendar.
Available Here: [https://counter.andrewdanielyoung.com](https://counter.andrewdanielyoung.com)
Generates countdown timers based on the closest or current event on your google calendar.

## Permissions

Granting the website access to your google account only allows it to view calendars. All data is kept locally and never saved.

[script.js](./script.js) only makes two server calls, both to the Google Calendar API.
Available here.

## Usage Details

Countdown-Timer will display how much time remains until an active event ends, or in lack of a current event, how long until your next event.

Using Google Calendar's API, the application will query your default calendar. Switching calendars is unavailable at the moment.

If two events are active, the timer will default to the event that began first.

Colors can be changed to user preference and the second page of the settings, to the right.

## Further Use

The API key for google calendar is only valid from the URL [https://counter.andrewdanielyoung.com](https://counter.andrewdanielyoung.com), so sadly the code can't be removed from that location.

You can get your own API keys [here (Google API Dashboard)](https://console.developers.google.com/apis/dashboard).

Google requires you to have a *https* website. Best way I found to get this up and running was to use [CertBot](https://certbot.eff.org/).

They turned the whole process into just a few lines into command line.

## Inspiration and Resources

This website was inspired by [Bell.Plus](https://bell.plus/lahs), which I used in highschool. However I wanted to be able to have it linked to my Google Calendar.

The circular timer was previously a CSS effect found [here (HOW TO: Pure CSS pie charts w/ CSS variables)](https://codeburst.io/how-to-pure-css-pie-charts-w-css-variables-38287aea161e).

It has since then been changed to a canvas element using the P5 library to draw. [Reference here.](https://p5js.org/examples/form-pie-chart.html)

[Google's Guide to Calendar API in Javascript](https://developers.google.com/calendar/quickstart/js)

[Resources for Google Calendar's API](https://developers.google.com/calendar/v3/reference/)


## License

[Available under the MIT license.](./LICENSE.txt)
---

Countdown timer will display how much time remains until an active event ends, or in lack of a current event, how long until your next event.

Using Google Calendar's API, the application will query your default calendar. More calendars can be added or removed.

In the case of overlapping events, countdown timer will look for the next begining or end of an event.

## Permisions

---

Granting the website access to your google account allows it to view calendars. The only data saved is your username, colors, clock type, text type, and ids of calenders you wish to enable on the app.

```
const schema = new mongoose.Schema({
username: {
type: String,
},
colors: {
type: Number,
default: 0,
},
calendars: [
{
type: String,
}
],
clockType: {
type: String,
default: 'pie',
},
textType: {
type: String,
default: 'verbouse',
},
});
```

Giving access to **email** is the only way to access your email address, so that your account can be directly tied to your email address and preferences accessable on any device. Besides using your email address as an ID, no other data relevant to your email is used.
50 changes: 50 additions & 0 deletions api/auth-check.ts
@@ -0,0 +1,50 @@
import {
NowRequest,
NowResponse,
} from '@vercel/node';
import mongoose from 'mongoose';

import { parseCookie } from '../util/auth';
import { userExists } from '../models/user';

const {
MONGO_USER,
MONGO_PASSWORD,
} = process.env;

mongoose.connect(`mongodb+srv://${MONGO_USER}:${MONGO_PASSWORD}@countdown-timer.fijoq.mongodb.net/countdown-timer?retryWrites=true&w=majority`, {
useNewUrlParser: true,
useUnifiedTopology: true,
});

/**
* Auth Check
* Verifies if user is already logged in.
* @param {NowRequest} req
* @param {NowResponse} res
* @returns {Object} Logged In Status
*/
export default async function (req: NowRequest, res: NowResponse) {
try {
const cookie = await parseCookie(req);

if (cookie) {
const { userID } = cookie;

if (await userExists(userID)) {
await mongoose.connection.close();

return res.status(200).send({ valid: true });
}
}
await mongoose.connection.close();

return res.status(200).send({ valid: false });
} catch (error) {
console.log(error);

await mongoose.connection.close();

return res.status(400).send('U broke it');
}
};
17 changes: 17 additions & 0 deletions api/auth-url.ts
@@ -0,0 +1,17 @@
import {
NowRequest,
NowResponse,
} from '@vercel/node';

import { getAuthURL } from '../util/google';

/**
* Auth URL
* Returns a URL by which the user can sign in via Google
* @param {NowRequest} req
* @param {NowResponse} res
* @returns {String} Google Auth URL
*/
export default async function (req: NowRequest, res: NowResponse) {
return res.status(200).send(await getAuthURL());
}
67 changes: 67 additions & 0 deletions api/authenticate.ts
@@ -0,0 +1,67 @@
import {
NowRequest,
NowResponse,
} from '@vercel/node';
import mongoose from 'mongoose';

import {
User,
defaultUserObject,
userExists,
} from '../models/user';
import {
getCalendar,
getUserID,
getToken,
} from '../util/google';
import { generateToken } from '../util/auth';

const {
MONGO_USER,
MONGO_PASSWORD,
} = process.env;

mongoose.connect(`mongodb+srv://${MONGO_USER}:${MONGO_PASSWORD}@countdown-timer.fijoq.mongodb.net/countdown-timer?retryWrites=true&w=majority`, {
useNewUrlParser: true,
useUnifiedTopology: true,
});

/**
* Authenticate
* Uses code to generate Google Auth Token and returns a user object.
* @param {NowRequest} req
* @param {NowResponse} res
* @returns {User} User data.
*/
export default async function (req: NowRequest, res: NowResponse) {
try {
const { code } = req.body;
const token = await getToken(code);

const calendar = await getCalendar(token);
const userID = await getUserID(calendar);

let user;
if (!await userExists(userID)) {
user = await defaultUserObject(userID);
await user.save();
} else {
user = await User.findOne({
username: userID,
});
}

let webToken = generateToken({
userID,
token,
}, "24h");

res.setHeader('Set-Cookie', [`cday-token=${webToken}; SameSite=Strict`]);
await mongoose.connection.close();
return res.status(200).send(user);
} catch (error) {
console.log(error);
await mongoose.connection.close();
return res.status(400).send('U broke it');
}
}
23 changes: 23 additions & 0 deletions api/calendars.ts
@@ -0,0 +1,23 @@
import { NowRequest, NowResponse } from '@vercel/node';

import { getCalendar } from '../util/google';

/**
* Calendars
* Returns a list of the user's calendars.
* @param {NowRequest} req
* @param {NowResponse} res
* @returns {Array<Calendar>} Array of Calendar Items
*/
export default async function (req: NowRequest, res: NowResponse) {
try {
const calendar = await getCalendar(req);

let response = await calendar.calendarList.list({});

return res.status(200).send(response.data.items);
} catch (error) {
console.log(error);
return res.status(400).send('U broke it');
}
};
56 changes: 56 additions & 0 deletions api/events.ts
@@ -0,0 +1,56 @@
import { NowRequest, NowResponse } from '@vercel/node';
import mongoose from 'mongoose';

import { getCalendar, getUserID } from '../util/google';
import { User } from '../models/user';

const {
MONGO_USER,
MONGO_PASSWORD,
} = process.env;

mongoose.connect(`mongodb+srv://${MONGO_USER}:${MONGO_PASSWORD}@countdown-timer.fijoq.mongodb.net/countdown-timer?retryWrites=true&w=majority`, {
useNewUrlParser: true,
useUnifiedTopology: true,
});

/**
* Events
* Returns an array of the User's Events
* @param {NowRequest} req
* @param {NowResponse} res
* @returns {Array<Event>} Array of Event Objects
*/
export default async function (req: NowRequest, res: NowResponse) {
try {
const calendar = await getCalendar(req);
const userID = await getUserID(calendar);

const user = await User.findOne({
username: userID,
});
const { calendars } = user || { calendars: ['primary'] };

let events = [];

let now = new Date();

for (let i = 0; i < calendars.length; i++) {
let response = await calendar.events.list({
calendarId: calendars[i],
timeMin: now.toISOString(),
maxResults: 30,
singleEvents: true,
orderBy: 'startTime',
});

events = events.concat(response.data.items);
}
await mongoose.connection.close();
return res.status(200).send(events);
} catch (error) {
console.log(error);
await mongoose.connection.close();
return res.status(400).send('U broke it');
}
};

1 comment on commit 084a8a1

@vercel
Copy link

@vercel vercel bot commented on 084a8a1 Oct 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.