diff --git a/.env b/.env
index c2ef068..854bba3 100644
--- a/.env
+++ b/.env
@@ -20,16 +20,17 @@ AZURE_AD_TENANT_ID=f69d1a93-bfba-498a-9b60-e87c1bc26276
# First APPLE_SECRET=eyJhbGciOiJFUzI1NiIsImtpZCI6IlRCM1YzNTVHNVkifQ.eyJhdWQiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiaXNzIjoiWEM1N1A5U1hESyIsImlhdCI6MTcxMjE3ODM0MiwiZXhwIjoxNzI3NzMwMzQzLCJzdWIiOiJjb20ubXdoaXRuZXNzaW5nLnNvZmlhIn0.XceA0qUQi0tXg0GM_LkJkpNU5AqXLiSB2JlEVbHCB_nINbQTWkjtoWxfqmvdOkIzwKtvdQ8FFb-crK9no9Bbbw
-APPLE_ID=com.mwhitnessing.sofia
-APPLE_SECRET=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IiJ9.eyJpYXQiOjE3MTMyMDI1MzAsImV4cCI6MTcyODc1NDUzMCwiYXVkIjoiaHR0cHM6Ly9hcHBsZWlkLmFwcGxlLmNvbSIsImlzcyI6IiIsInN1YiI6IiJ9.C18A_ZBGcaHoGf8JPeQtdkLdWQqAqzCygWd6eD_S-X3OXw8ZWNXGtGAk5xEB9sui84OW60dNnH6ZGQMAtP5-hA
-
-# with team in the ID?
-#APPLE_ID=XC57P9SXDK.com.mwhitnessing.sofia
-#APPLE_SECRET=eyJhbGciOiJFUzI1NiIsImtpZCI6IlRCM1YzNTVHNVkifQ.eyJhdWQiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiaXNzIjoiWEM1N1A5U1hESyIsImlhdCI6MTcxMjE3ODM0MiwiZXhwIjoxNzI3NzMwMzQzLCJzdWIiOiJjb20ubXdoaXRuZXNzaW5nLnNvZmlhIn0.XceA0qUQi0tXg0GM_LkJkpNU5AqXLiSB2JlEVbHCB_nINbQTWkjtoWxfqmvdOkIzwKtvdQ8FFb-crK9no9Bbbw
-# to generate
APPLE_TEAM_ID=XC57P9SXDK
APPLE_KEY_ID=TB3V355G5Y
+APPLE_APP_ID=com.mwhitnessing.sofia
+APPLE_SECRET=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlRCM1YzNTVHNVkifQ.eyJpYXQiOjE3MTMzMDQ1OTMsImV4cCI6MTcyODg1NjU5MywiYXVkIjoiaHR0cHM6Ly9hcHBsZWlkLmFwcGxlLmNvbSIsImlzcyI6IlhDNTdQOVNYREsiLCJzdWIiOiJjb20ubXdoaXRuZXNzaW5nLnNvZmlhIn0.iO2prjQ_4P7F17R7LTJfG9zHluj59uUtm8DA1LbK49jVBMeGHQP_Az7s_yU5D-GeMHSwU7VnVHcaVKiGWT_Yjg
+
+# with team in the ID?
+#APPLE_APP_ID=XC57P9SXDK.com.mwhitnessing.sofia
+#APPLE_SECRET=eyJhbGciOiJFUzI1NiIsImtpZCI6IlRCM1YzNTVHNVkifQ.eyJhdWQiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiaXNzIjoiWEM1N1A5U1hESyIsImlhdCI6MTcxMjE3ODM0MiwiZXhwIjoxNzI3NzMwMzQzLCJzdWIiOiJjb20ubXdoaXRuZXNzaW5nLnNvZmlhIn0.XceA0qUQi0tXg0GM_LkJkpNU5AqXLiSB2JlEVbHCB_nINbQTWkjtoWxfqmvdOkIzwKtvdQ8FFb-crK9no9Bbbw
+# to generate
+
AUTH0_ID=Aa9f3HJowauUrmBVY4iQzQJ7fYsaZDbK
AUTH0_SECRET=_c0O9GkyRXkoWMQW7jNExnl6UoXN6O4oD3mg7NZ_uHVeAinCUtcTAkeQmcKXpZ4x
diff --git a/.env.production b/.env.production
index 112901f..8e9e8f4 100644
--- a/.env.production
+++ b/.env.production
@@ -7,7 +7,7 @@ NEXT_PUBLIC_PUBLIC_URL= https://sofia.mwitnessing.com
NEXTAUTH_SECRET=1dd8a5457970d1dda50600be28e935ecc4513ff27c49c431849e6746f158d638
# ? do we need to duplicate this? already defined in the deoployment yml file
DATABASE=mysql://jwpwsofia:dwxhns9p9vp248V39xJyRthUsZ2gR9@mariadb:3306/jwpwsofia
-
+# DATABASE=mysql://cart:cartpw@localhost:3306/cart
EMAIL_BYPASS_TO=
MAILTRAP_HOST_BULK=bulk.smtp.mailtrap.io
diff --git a/_deploy/setupAppleId.mjs b/_deploy/setupAppleId.mjs
index ef22074..7ff3383 100644
--- a/_deploy/setupAppleId.mjs
+++ b/_deploy/setupAppleId.mjs
@@ -4,14 +4,14 @@ import { SignJWT } from "jose"
import { createPrivateKey } from "crypto"
if (process.argv.includes("--help") || process.argv.includes("-h")) {
- console.log(`
+ 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]
- APPLE_ID=com.mwhitnessing.sofia
+ APPLE_APP_ID=com.mwhitnessing.sofia
APPLE_TEAM_ID=XC57P9SXDK
APPLE_KEY_ID=TB3V355G5Y
APPLE_KEY
@@ -37,45 +37,45 @@ eyJhbGciOiJFUzI1NiIsImtpZCI6IlRCM1YzNTVHNVkifQ.eyJhdWQiOiJodHRwczovL2FwcGxlaWQuY
--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 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,
+ const {
+ team_id,
+ iss = team_id,
- private_key,
+ private_key,
- client_id,
- sub = client_id,
+ client_id,
+ sub = client_id,
- key_id,
- kid = key_id,
+ key_id,
+ kid = key_id,
- expires_in = 86400 * 180,
- exp = Math.ceil(Date.now() / 1000) + expires_in,
- } = args
+ 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(`
+ /**
+ * 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")))}`)
+ .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/PwaManager.tsx b/components/PwaManager.tsx
index 1eae6ce..3bce4fc 100644
--- a/components/PwaManager.tsx
+++ b/components/PwaManager.tsx
@@ -151,7 +151,7 @@ function PwaManager() {
return;
}
- await fetch('/api/notification', {
+ await fetch('/api/notify', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@@ -219,6 +219,11 @@ function PwaManager() {
Телеграм
+
signIn()}>
+
signIn()}>
);
diff --git a/package-lock.json b/package-lock.json
index 9acc5d5..a8be91d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4802,6 +4802,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
"integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+ "optional": true,
"dependencies": {
"clean-stack": "^2.0.0",
"indent-string": "^4.0.0"
@@ -5771,6 +5772,7 @@
"url": "https://github.com/sponsors/sibiraj-s"
}
],
+ "optional": true,
"engines": {
"node": ">=8"
}
@@ -5784,6 +5786,7 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
"integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+ "optional": true,
"engines": {
"node": ">=6"
}
@@ -8546,7 +8549,8 @@
"node_modules/hosted-git-info": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
- "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="
+ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
+ "optional": true
},
"node_modules/hsl-to-hex": {
"version": "1.0.0",
@@ -8888,6 +8892,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "optional": true,
"engines": {
"node": ">=8"
}
@@ -8915,6 +8920,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz",
"integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==",
+ "optional": true,
"engines": {
"node": ">=10"
}
@@ -10943,6 +10949,7 @@
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
"integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+ "optional": true,
"dependencies": {
"hosted-git-info": "^2.1.4",
"resolve": "^1.10.0",
@@ -10954,6 +10961,7 @@
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+ "optional": true,
"bin": {
"semver": "bin/semver"
}
@@ -13870,6 +13878,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
"integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+ "optional": true,
"dependencies": {
"aggregate-error": "^3.0.0"
},
@@ -15871,6 +15880,7 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
"integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
+ "optional": true,
"dependencies": {
"spdx-expression-parse": "^3.0.0",
"spdx-license-ids": "^3.0.0"
@@ -15879,12 +15889,14 @@
"node_modules/spdx-exceptions": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
- "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="
+ "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
+ "optional": true
},
"node_modules/spdx-expression-parse": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
"integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "optional": true,
"dependencies": {
"spdx-exceptions": "^2.1.0",
"spdx-license-ids": "^3.0.0"
@@ -15893,7 +15905,8 @@
"node_modules/spdx-license-ids": {
"version": "3.0.17",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz",
- "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg=="
+ "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==",
+ "optional": true
},
"node_modules/ssf": {
"version": "0.8.2",
@@ -17442,6 +17455,7 @@
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "optional": true,
"dependencies": {
"spdx-correct": "^3.0.0",
"spdx-expression-parse": "^3.0.0"
diff --git a/pages/api/auth/apple-signin.ts b/pages/api/auth/apple-signin.ts
new file mode 100644
index 0000000..7fd9bf1
--- /dev/null
+++ b/pages/api/auth/apple-signin.ts
@@ -0,0 +1,45 @@
+// pages/api/auth/apple.js
+import jwt from 'jsonwebtoken';
+import axios from 'axios';
+import fs from 'fs';
+import path from 'path';
+
+const dotenv = require("dotenv");
+
+export default async function handler(req, res) {
+ if (req.method === 'GET') {
+ // Generate the client secret
+ const clientSecret = generateClientSecret();
+ // const redirectUri = `${req.headers.origin}/api/auth/apple/callback`;
+ const redirectUri = `https://sofia.mwitnessing.com/api/auth/callback/apple`;
+
+ // Redirect to Apple's authorization page
+ const url = `https://appleid.apple.com/auth/authorize?response_type=code&client_id=${process.env.APPLE_APP_ID}&redirect_uri=${encodeURIComponent(redirectUri)}&scope=email&response_mode=form_post&state=initial&usePopup=true&client_secret=${encodeURIComponent(clientSecret)}`;
+ res.redirect(url);
+ } else {
+ res.status(405).json({ error: 'Method not allowed' });
+ }
+}
+
+function generateClientSecret() {
+ const appleKey = fs.readFileSync(path.resolve('./_deploy/appleKey.p8'), 'utf8');
+ const teamID = process.env.APPLE_TEAM_ID || "XC57P9SXDK";
+ const keyID = process.env.APPLE_KEY_ID || "TB3V355G5Y";
+ const appleAppID = process.env.APPLE_APP_ID;
+
+ // Token expiration
+ const now = Math.floor(Date.now() / 1000);
+ const exp = now + 86400 * 180; // 6 months
+
+ const claims = {
+ iss: teamID,
+ iat: now,
+ exp: exp,
+ aud: 'https://appleid.apple.com',
+ sub: appleAppID,
+ };
+
+ const token = jwt.sign(claims, appleKey, { algorithm: 'ES256', header: { alg: 'ES256', kid: keyID } });
+ console.log("generated new token:" + token);
+ return token;
+}
diff --git a/pages/api/auth/apple-token.ts b/pages/api/auth/apple-token.ts
index 128c34a..dd44751 100644
--- a/pages/api/auth/apple-token.ts
+++ b/pages/api/auth/apple-token.ts
@@ -9,17 +9,19 @@ export default async function handler(req, res) {
if (req.method === 'GET') {
try {
const appleKey = fs.readFileSync(path.resolve('./_deploy/appleKey.p8'), 'utf8');
-
+ const teamID = process.env.APPLE_TEAM_ID || "XC57P9SXDK";
+ const keyID = process.env.APPLE_KEY_ID || "TB3V355G5Y";
+ const appleAppID = process.env.APPLE_APP_ID || "com.mwitnessing.mwitnessing";
const token = jwt.sign({}, appleKey, {
algorithm: 'ES256',
expiresIn: '180d',
- issuer: process.env.APPLE_TEAM_ID,
+ issuer: teamID,
header: {
alg: 'ES256',
- kid: process.env.APPLE_KEY_ID,
+ kid: keyID,
},
audience: 'https://appleid.apple.com',
- subject: process.env.APPLE_ID,
+ subject: appleAppID,
});
// Redirect to Apple's authentication page, or send the token to the client to do so
diff --git a/pages/api/index.ts b/pages/api/index.ts
index f01dfa3..3349dad 100644
--- a/pages/api/index.ts
+++ b/pages/api/index.ts
@@ -30,7 +30,7 @@ export default async function handler(req, res) {
var action = req.query.action;
var filter = req.query.filter;
- let day: Date, monthInfo: any;
+ let day: Date;
let isExactTime;
if (req.query.date) {
day = new Date(req.query.date);
@@ -42,6 +42,7 @@ export default async function handler(req, res) {
isExactTime = true;
}
+ let monthInfo = common.getMonthDatesInfo(day);
const searchText = req.query.searchText?.normalize('NFC');
try {
@@ -220,6 +221,7 @@ export default async function handler(req, res) {
res.status(200).json(shiftsForDate);
break;
+
case "copyOldAvailabilities":
//get all publishers that don't have availabilities for the current month
monthInfo = common.getMonthDatesInfo(day);
@@ -283,12 +285,12 @@ export default async function handler(req, res) {
type: AvailabilityType.Monthly,
isFromPreviousMonth: true,
name: avail.name || "старо предпочитание",
- // parentAvailabilityId: avail.id
- parentAvailability: {
- connect: {
- id: avail.id
- }
- }
+ parentAvailabilityId: avail.id,
+ // parentAvailability: {
+ // connect: {
+ // id: avail.id
+ // }
+ // },
}
await prisma.availability.create({ data: data });
@@ -331,7 +333,6 @@ export default async function handler(req, res) {
case "updateShifts":
//get all shifts for the month and publish them (we pass date )
- let monthInfo = common.getMonthDatesInfo(day);
let isPublished = common.parseBool(req.query.isPublished);
let updated = await prisma.shift.updateMany({
where: {
diff --git a/pages/api/notify.ts b/pages/api/notify.ts
new file mode 100644
index 0000000..34ebd93
--- /dev/null
+++ b/pages/api/notify.ts
@@ -0,0 +1,37 @@
+
+const webPush = require('web-push')
+
+webPush.setVapidDetails(
+ `mailto:${process.env.WEB_PUSH_EMAIL}`,
+ process.env.NEXT_PUBLIC_WEB_PUSH_PUBLIC_KEY,
+ process.env.WEB_PUSH_PRIVATE_KEY
+)
+
+const Notification = (req, res) => {
+ if (req.method == 'POST') {
+ const { subscription } = req.body
+
+ webPush
+ .sendNotification(
+ subscription,
+ JSON.stringify({ title: 'Hello Web Push', message: 'Your web push notification is here!' })
+ )
+ .then(response => {
+ res.writeHead(response.statusCode, response.headers).end(response.body)
+ })
+ .catch(err => {
+ if ('statusCode' in err) {
+ res.writeHead(err.statusCode, err.headers).end(err.body)
+ } else {
+ console.error(err)
+ res.statusCode = 500
+ res.end()
+ }
+ })
+ } else {
+ res.statusCode = 405
+ res.end()
+ }
+}
+
+export default Notification
diff --git a/pages/dash.tsx b/pages/dash.tsx
index 71718db..fb3a2f7 100644
--- a/pages/dash.tsx
+++ b/pages/dash.tsx
@@ -88,6 +88,7 @@ async function getAvailabilities(userId) {
name: true,
isActive: true,
isFromPreviousAssignment: true,
+ isFromPreviousMonth: true,
dayofweek: true,
dayOfMonth: true,
startTime: true,
diff --git a/server.js b/server.js
index 2c41621..88c5b7b 100644
--- a/server.js
+++ b/server.js
@@ -40,7 +40,7 @@ console.log("process.env.PORT = ", process.env.PORT);
console.log("process.env.TELEGRAM_BOT = ", process.env.TELEGRAM_BOT);
console.log("process.env.DATABASE_URL = ", process.env.DATABASE_URL);
console.log("process.env.DATABASE = ", process.env.DATABASE);
-console.log("process.env.APPLE_ID = ", process.env.APPLE_ID);
+console.log("process.env.APPLE_APP_ID = ", process.env.APPLE_APP_ID);
//require('module-alias/register');