From f06fed455b62d7256b1531521601b30a20fd2e18 Mon Sep 17 00:00:00 2001 From: Dobromir Popov Date: Mon, 15 Apr 2024 22:12:35 +0300 Subject: [PATCH] (wip) - show repeating events in calendar (admin) script to get jwt signature for Apple ID --- _doc/apple-gen-secret.mjs | 67 --------------------- components/availability/AvailabilityForm.js | 2 +- components/calendar/avcalendar.tsx | 18 +++--- pages/api/auth/apple-token.ts | 40 ++++++++++++ pages/api/index.ts | 7 ++- pages/cart/calendar/index.tsx | 18 +++--- src/helpers/common.js | 23 ++++--- src/helpers/data.js | 2 +- 8 files changed, 80 insertions(+), 97 deletions(-) delete mode 100644 _doc/apple-gen-secret.mjs create mode 100644 pages/api/auth/apple-token.ts diff --git a/_doc/apple-gen-secret.mjs b/_doc/apple-gen-secret.mjs deleted file mode 100644 index 4a6600c..0000000 --- a/_doc/apple-gen-secret.mjs +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/node -# https://gist.githubusercontent.com/balazsorban44/09613175e7b37ec03f676dcefb7be5eb/raw/b0d31aa0c7f58e0088fdf59ec30cad1415a3475b/apple-gen-secret.mjs - -import { SignJWT } from "jose" -import { createPrivateKey } from "crypto" - -if (process.argv.includes("--help") || process.argv.includes("-h")) { - console.log(` - Creates a JWT from the components found at Apple. - By default, the JWT has a 6 months expiry date. - Read more: https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens#3262048 - - Usage: - node apple.mjs [--kid] [--iss] [--private_key] [--sub] [--expires_in] [--exp] - - Options: - --help Print this help message - --kid, --key_id The key id of the private key - --iss, --team_id The Apple team ID - --private_key The private key to use to sign the JWT. (Starts with -----BEGIN PRIVATE KEY-----) - --sub, --client_id The client id to use in the JWT. - --expires_in Number of seconds from now when the JWT should expire. Defaults to 6 months. - --exp Future date in seconds when the JWT expires - `) -} else { - const args = process.argv.slice(2).reduce((acc, arg, i) => { - if (arg.match(/^--\w/)) { - const key = arg.replace(/^--/, "").toLowerCase() - acc[key] = process.argv[i + 3] - } - return acc - }, {}) - - const { - team_id, - iss = team_id, - - private_key, - - client_id, - sub = client_id, - - key_id, - kid = key_id, - - expires_in = 86400 * 180, - exp = Math.ceil(Date.now() / 1000) + expires_in, - } = args - - /** - * How long is the secret valid in seconds. - * @default 15780000 - */ - const expiresAt = Math.ceil(Date.now() / 1000) + expires_in - const expirationTime = exp ?? expiresAt - console.log(` -Apple client secret generated. Valid until: ${new Date(expirationTime * 1000)} - -${await new SignJWT({}) - .setAudience("https://appleid.apple.com") - .setIssuer(iss) - .setIssuedAt() - .setExpirationTime(expirationTime) - .setSubject(sub) - .setProtectedHeader({ alg: "ES256", kid }) - .sign(createPrivateKey(private_key.replace(/\\n/g, "\n")))}`) -} \ No newline at end of file diff --git a/components/availability/AvailabilityForm.js b/components/availability/AvailabilityForm.js index 3da3063..66262ee 100644 --- a/components/availability/AvailabilityForm.js +++ b/components/availability/AvailabilityForm.js @@ -298,7 +298,7 @@ export default function AvailabilityForm({ publisherId, existingItems, inline, o return itemStart && itemEnd && (slotStart.getTime() < itemEnd.getTime()) && - (slotEnd.getTime() > itemStart.getTime()); + (slotEnd.getTime() >= itemStart.getTime()); }); slots.push({ diff --git a/components/calendar/avcalendar.tsx b/components/calendar/avcalendar.tsx index 4fb3e2a..4bc8225 100644 --- a/components/calendar/avcalendar.tsx +++ b/components/calendar/avcalendar.tsx @@ -17,7 +17,7 @@ import { MdToday } from 'react-icons/md'; import { useSwipeable } from 'react-swipeable'; import axiosInstance from '../../src/axiosSecure'; // import { set, format, addDays } from 'date-fns'; -import { isEqual, isSameDay, getHours, getMinutes } from 'date-fns'; +// import { isEqual, isSameDay, getHours, getMinutes } from 'date-fns'; import { filter } from 'jszip'; import e from 'express'; @@ -170,6 +170,7 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => { }; const filterEvents = (evts, publisherId, startdate) => { setDate(startdate); // Assuming setDate is a function that sets some state or context + const filterHHmm = new Date(startdate).getHours() * 100 + new Date(startdate).getMinutes(); // Filter events based on the publisher ID and the start date/time const existingEvents = evts?.filter(event => { @@ -177,15 +178,15 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => { const isPublisherMatch = (event.publisher?.id || event.publisherId) === publisherId; let isDateMatch; + const eventDate = new Date(event.startTime); if (event.repeatWeekly && event.date) { // Compare only the time part - const eventDate = new Date(event.startTime); - const isSameHour = getHours(eventDate) === getHours(startdate); - const isSameMinute = getMinutes(eventDate) === getMinutes(startdate); - isDateMatch = isSameHour && isSameMinute; + let evenStarttHHmm = eventDate.getHours() * 100 + eventDate.getMinutes(); + let eventEndHHmm = eventDate.getHours() * 100 + eventDate.getMinutes(); + isDateMatch = filterHHmm >= evenStarttHHmm && filterHHmm <= eventEndHHmm; } else if (event.date) { - // Compare the full date - isDateMatch = isSameDay(new Date(event.date), startdate); + // Compare the full date. issameday is not working. do it manually + isDateMatch = eventDate.setHours(0, 0, 0, 0) === new Date(startdate).setHours(0, 0, 0, 0); } return isPublisherMatch && isDateMatch; @@ -203,7 +204,7 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => { maxTime.setHours(maxHour, 0, 0); const totalHours = maxHour - minHour; - const handleSelect = ({ start, end }) => { + const handleSelect = ({ mode, start, end }) => { const startdate = typeof start === 'string' ? new Date(start) : start; const enddate = typeof end === 'string' ? new Date(end) : end; @@ -230,6 +231,7 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => { setDate(start); // get exising events for the selected date + //ToDo: properly fix this. filterEvents does not return the expcted results let existingEvents = filterEvents(evts, publisherId, startdate); // if existingEvents is empty - create new with the selected range if (existingEvents.length === 0) { diff --git a/pages/api/auth/apple-token.ts b/pages/api/auth/apple-token.ts new file mode 100644 index 0000000..128c34a --- /dev/null +++ b/pages/api/auth/apple-token.ts @@ -0,0 +1,40 @@ +// pages/api/auth/apple-token.js +import jwt from 'jsonwebtoken'; +import fs from 'fs'; +import path from 'path'; +const dotenv = require("dotenv"); + + +export default async function handler(req, res) { + if (req.method === 'GET') { + try { + const appleKey = fs.readFileSync(path.resolve('./_deploy/appleKey.p8'), 'utf8'); + + const token = jwt.sign({}, appleKey, { + algorithm: 'ES256', + expiresIn: '180d', + issuer: process.env.APPLE_TEAM_ID, + header: { + alg: 'ES256', + kid: process.env.APPLE_KEY_ID, + }, + audience: 'https://appleid.apple.com', + subject: process.env.APPLE_ID, + }); + + // Redirect to Apple's authentication page, or send the token to the client to do so + console.log(token); + res.status(200).send({ + message: 'Generated token for Apple Sign In', + token: token + }); + } catch (error) { + console.error('Error signing in with Apple:', error); + res.status(500).send({ error: 'Failed to sign in with Apple' }); + } + } else { + // Handle any non-GET requests + res.setHeader('Allow', ['GET']); + res.status(405).end(`Method ${req.method} Not Allowed`); + } +} diff --git a/pages/api/index.ts b/pages/api/index.ts index a75d8ef..f01dfa3 100644 --- a/pages/api/index.ts +++ b/pages/api/index.ts @@ -161,8 +161,9 @@ export default async function handler(req, res) { res.status(200).json(publishers); break; case "filterPublishersNew": + let includeOldAvailabilities = common.parseBool(req.query.includeOldAvailabilities); let results = await filterPublishersNew_Available(req.query.select, day, - common.parseBool(req.query.isExactTime), common.parseBool(req.query.isForTheMonth)); + common.parseBool(req.query.isExactTime), common.parseBool(req.query.isForTheMonth), includeOldAvailabilities); res.status(200).json(results); break; @@ -461,8 +462,8 @@ export async function getMonthlyStatistics(selectFields, filterDate) { } -export async function filterPublishersNew_Available(selectFields, filterDate, isExactTime = false, isForTheMonth = false, isWithStats = true) { - return data.filterPublishersNew(selectFields, filterDate, isExactTime, isForTheMonth, isWithStats); +export async function filterPublishersNew_Available(selectFields, filterDate, isExactTime = false, isForTheMonth = false, isWithStats = true, includeOldAvailabilities = false) { + return data.filterPublishersNew(selectFields, filterDate, isExactTime, isForTheMonth, isWithStats, includeOldAvailabilities); } // availabilites filter: diff --git a/pages/cart/calendar/index.tsx b/pages/cart/calendar/index.tsx index a04a1f4..6bdacb6 100644 --- a/pages/cart/calendar/index.tsx +++ b/pages/cart/calendar/index.tsx @@ -76,15 +76,15 @@ export default function CalendarPage({ initialEvents, initialShifts }) { const [modalPub, setModalPub] = useState(null); // ------------------ no assignments checkbox ------------------ - const [isCheckboxChecked, setIsCheckboxChecked] = useState(false); + const [filterShowWithoutAssignments, setFilterShowWithoutAssignments] = useState(false); const handleCheckboxChange = (event) => { - setIsCheckboxChecked(!isCheckboxChecked); // Toggle the checkbox state + setFilterShowWithoutAssignments(!filterShowWithoutAssignments); // Toggle the checkbox state }; useEffect(() => { - console.log("checkbox checked: " + isCheckboxChecked); + console.log("checkbox checked: " + filterShowWithoutAssignments); handleCalDateChange(value); // Call handleCalDateChange whenever isCheckboxChecked changes - }, [isCheckboxChecked]); // Dependency array + }, [filterShowWithoutAssignments]); // Dependency array const [selectedMonth, setSelectedMonth] = useState(new Date().getMonth()); useEffect(() => { @@ -99,7 +99,7 @@ export default function CalendarPage({ initialEvents, initialShifts }) { var date = new Date(common.getDateFromDateTime(selectedDate));//ToDo: check if seting the timezone affects the selectedDate?! var dateStr = common.getISODateOnly(date); console.log("Setting date to '" + date.toLocaleDateString() + "' from '" + selectedDate.toLocaleDateString() + "'. ISO: " + date.toISOString(), "locale ISO:", common.getISODateOnly(date)); - if (isCheckboxChecked) { + if (filterShowWithoutAssignments) { console.log(`getting unassigned publishers for ${common.getMonthName(date.getMonth())} ${date.getFullYear()}`); const { data: availablePubsForDate } = await axiosInstance.get(`/api/?action=getUnassignedPublishers&date=${dateStr}&select=id,firstName,lastName,isActive,desiredShiftsPerMonth`); setAvailablePubs(availablePubsForDate); @@ -110,7 +110,7 @@ export default function CalendarPage({ initialEvents, initialShifts }) { const { data: shiftsForDate } = await axiosInstance.get(`/api/?action=getShiftsForDay&date=${dateStr}`); setShifts(shiftsForDate); setIsPublished(shiftsForDate.some(shift => shift.isPublished)); - let { data: availablePubsForDate } = await axiosInstance.get(`/api/?action=filterPublishersNew&date=${dateStr}&select=id,firstName,lastName,isActive,desiredShiftsPerMonth`); + let { data: availablePubsForDate } = await axiosInstance.get(`/api/?action=filterPublishersNew&date=${dateStr}&select=id,firstName,lastName,isActive,desiredShiftsPerMonth&includeOldAvailabilities=${filterShowWithoutAssignments}`); availablePubsForDate.forEach(pub => { pub.canTransport = pub.availabilities.some(av => @@ -664,9 +664,10 @@ export default function CalendarPage({ initialEvents, initialShifts }) {

Достъпни за този ден: {availablePubs.length}