Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

286 unlock 4 implement key loyalty card contract #292

Merged
merged 36 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
c0647e0
loyalty-card: actions and creating happy path
AlexandreG-tech Mar 1, 2024
612d19b
unfreeze pnpm lock
AlexandreG-tech Apr 12, 2024
22c057e
mockServiceWorker fix
AlexandreG-tech Apr 12, 2024
7d8cf54
Revert "loyalty card cron : add ignore path pattern in jest config"
AlexandreG-tech Apr 12, 2024
01394fe
fix toZonedTime
AlexandreG-tech Apr 12, 2024
3abaf37
fix testPathIgnorePatterns in jest config loyalty card cron
AlexandreG-tech Apr 12, 2024
b1fb7a4
♻️ (shopify feature): dynamically import OffKeyProfile & OffKeyGate f…
sebpalluel Apr 12, 2024
8c3d6ef
📝 Add project context, tech stack, and code standards to .cursorrules
sebpalluel Apr 15, 2024
ea9623b
♻️ Refactor project configs to remove `debug` option for consistency
sebpalluel Apr 15, 2024
4c6fec7
📝 (examples.tsx): fix formatting by removing extra space in import st…
sebpalluel Apr 15, 2024
5ca174c
✅ (eventPassActivity.spec.ts): update test to return empty array inst…
sebpalluel Apr 15, 2024
4138b7d
✨ (jest.integration.config.ts): add 'mjs' to moduleFileExtensions to …
sebpalluel Apr 15, 2024
1a50aba
✨ (processLoyaltyCardsMint.ts): add try-catch block for better error …
sebpalluel Apr 15, 2024
1a37297
moved all packages from CommonJs to esnext (fix next turbo issue)
sebpalluel Apr 15, 2024
f6f8478
moved all packages from CommonJs to esnext (fix next turbo issue)
sebpalluel Apr 15, 2024
514c688
⬆️ (pnpm-lock.yaml): update lockfile version and remove deprecated se…
sebpalluel Apr 16, 2024
c24aa29
♻️ (loyalty-card): rename import from deployLoyaltyCardCollectionWrap…
sebpalluel Apr 16, 2024
783aa9a
✨ (LoyaltyCardNftsPasswordsTable/examples.tsx): limit mock data to fi…
sebpalluel Apr 16, 2024
d7e4a99
✨ (.env.local, index.mjs): add SHOPIFY_SHARED_SECRET to support Shopi…
sebpalluel Apr 16, 2024
83e2512
♻️ Refactor TypeScript interfaces to types for consistency across pro…
sebpalluel Apr 17, 2024
26bff9e
✨ (shopify/loyalty-card route): Add error handling with custom error …
sebpalluel Apr 18, 2024
b8addb8
⬆️ (package.json): upgrade pnpm version from 9.0.1 to 9.0.2 to ensure…
sebpalluel Apr 18, 2024
105a383
add shopify webhook route for checkout-paid
AlexandreG-tech Apr 18, 2024
7ef7687
🔧 (.npmrc): add .npmrc file with auto-install-peers set to false to p…
sebpalluel Apr 18, 2024
90d7fc7
♻️ (crypto lib): remove console.log from isValidSignature for cleaner…
sebpalluel Apr 19, 2024
5adf4c0
✨ (shopify/index.ts, validators.ts): Introduce MintLoyaltyCardWithCus…
sebpalluel Apr 19, 2024
c35638d
✨ (migrations): Simplify shopifyDomain table structure, use domain as PK
sebpalluel Apr 19, 2024
804aceb
♻️ (shopify loyalty-card route): simplify error handling by removing …
sebpalluel Apr 19, 2024
1d766b4
add maxWorkers to not make multiple integration tests collide when in…
AlexandreG-tech Apr 19, 2024
c65fac0
change package manager version
AlexandreG-tech Apr 19, 2024
a4121f9
fix loyalty card cron tests
AlexandreG-tech Apr 19, 2024
24cf298
🔥 Remove loyaltyCardNftContract migration files to revert schema changes
sebpalluel Apr 22, 2024
4777653
⬆️ (package.json): update packageManager field to use caret versionin…
sebpalluel Apr 22, 2024
e4aeb6a
♻️ (mockServiceWorker.js): remove semicolons for consistency with pro…
sebpalluel Apr 22, 2024
7d68a1f
♻️ (mockServiceWorker.js): Refactor to add missing semicolons and imp…
sebpalluel Apr 22, 2024
45297fa
⬆️ (package.json): downgrade minimum required pnpm version from 9.0.4…
sebpalluel Apr 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
18 changes: 18 additions & 0 deletions .cursorrules
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Project Context

- Name: Offline - Next-gen Consumer Brand Interaction
- Description: At Offline, we are developing innovative solutions to enhance brand-customer interactions through user-centric applications using blockchain and NFT technologies. The project consists of three main apps: the web platform for users, a back-office for companies and organizers, and a micro-app called 'unlock' for token gating on Shopify pages and potential future vendor integrations.

# Technology Stack

- Frontend: Next.js 14, Storybook for component development
- Backend: Hasura/Postgres for database operations
- Testing: Jest for unit and integration tests; Playwright for end-to-end testing
- Architecture: Nx monorepo setup with clean domain models. Libraries are organized within the 'libs' folder.

# Code Standards

- Clean Code: Follow SOLID principles and ensure code is DRY and easily testable.
- Typing: Employ strict TypeScript typing to ensure robustness and maintainability.
- Testing: Aim for high test coverage with well-thought-out unit, integration, and E2E tests.
- Documentation: Maintain clear and comprehensive documentation, especially for public APIs and complex logic.
25 changes: 16 additions & 9 deletions .env.local
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ NX_VERCEL_REMOTE_CACHE_TOKEN=8HfZAvj1ZCUEYLuNISczuuF2
NX_VERCEL_REMOTE_CACHE_TEAM=team_cNOd6z2HTsiaB0cibAjaQSIM

# hasura
HASURA_VERSION=v2.35.0
HASURA_VERSION=v2.38.0
HASURA_GRAPHQL_SERVER_PORT=8080
HASURA_GRAPHQL_ADMIN_SECRET=password
HASURA_CONSOLE_PORT=9695
Expand Down Expand Up @@ -39,13 +39,13 @@ NEXTAUTH_SECRET="-----BEGIN RSA PRIVATE KEY-----\nMIIJKQIBAAKCAgEAtHmpqKfTdp2DNn
TOKEN_LIFE_TIME="648000"

## web3
## Here we take the Mumbai testnet
NEXT_PUBLIC_CHAIN='80001'
CHAIN='80001'
## Here we take the Amoy testnet
NEXT_PUBLIC_CHAIN='80002'
CHAIN='80002'

## Alchemy
NEXT_PUBLIC_ALCHEMY_API_KEY='aTwl7l6SRRC22e-7joQbT7oMG6yTjUm0'
ALCHEMY_API_KEY='aTwl7l6SRRC22e-7joQbT7oMG6yTjUm0'
NEXT_PUBLIC_ALCHEMY_API_KEY='o-0Gv1BvrP4mEFNNQEQziQvc7SI3T7rm'
ALCHEMY_API_KEY='o-0Gv1BvrP4mEFNNQEQziQvc7SI3T7rm'
ALCHEMY_AUTH_TOKEN='dGlcmvJsh6Oac3KFJFok2r0R2UgTrcYm'


Expand All @@ -62,6 +62,10 @@ EMAIL_WEB_UI=8025
WEB_APP_URL='https://www.staging.offline.live/'
NEXT_PUBLIC_WEB_APP_URL='https://www.staging.offline.live/'

## API

API_SECRET_ENCRYPTION_KEY="5557ed70d7c542efa7d9b23516a3baed"

# VIRTUAL_HOST=example.com
# LETSENCRYPT_HOST=example.com
# [email protected]
Expand Down Expand Up @@ -127,9 +131,12 @@ POSTHOG_PERSONAL_API_KEY=phx_wIGzuBaUWMAdzBpxpkil4yIDdINhy5FfnFR4QYhUj6q
NEXT_PUBLIC_POSTHOG_KEY=phc_FcjrV3dP2qG5qVafKh6ULkTYGq5FWD5mT8XPk7t9Y8Q

# Cometh
NEXT_PUBLIC_COMETH_CONNECT_API_KEY=xPVZx4OVf7oR9O2yOI5zTOgkatkkzF6W
COMETH_CONNECT_API_KEY=xPVZx4OVf7oR9O2yOI5zTOgkatkkzF6W
NEXT_PUBLIC_COMETH_CONNECT_API_KEY=vDMJtXRUsdDVCXJ0GBAaKMTjuebI5S3Y
COMETH_CONNECT_API_KEY=vDMJtXRUsdDVCXJ0GBAaKMTjuebI5S3Y

# Wallet connect
NEXT_PUBLIC_WC_PROJECT_ID=68b34422801cb3e8ea1eb7f823266c28
NEXT_PUBLIC_WC_RELAY_URL=wss://relay.walletconnect.com
NEXT_PUBLIC_WC_RELAY_URL=wss://relay.walletconnect.com

# Shopify
SHOPIFY_SHARED_SECRET=c886ebdff67650455049c4cc52517c0d
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
auto-install-peers=false
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ async function EventSheetPageContent({
if (!user || !user?.role) return notFound();
const event = await getEventWithPassesOrganizer({ slug: eventSlug, locale });
if (!event) return notFound();
return <EventSheet event={event} organizerId={user.role.organizerId} />;
return <EventSheet event={event} organizerId={user.role?.organizerId} />;
}

interface EventSheetPageProps {
Expand Down
5 changes: 5 additions & 0 deletions apps/back-office/app/[locale]/loyalty-card/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { LoyaltyCardSkeleton } from '@features/back-office/loyalty-card';

export default function LoyaltyCardLoading() {
return <LoyaltyCardSkeleton />;
}
16 changes: 16 additions & 0 deletions apps/back-office/app/[locale]/loyalty-card/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { LoyaltyCardPage } from '@features/back-office/loyalty-card';
import { getLoyaltyCardOrganizer } from '@features/back-office/loyalty-card-api';
import { type Locale } from '@next/i18n';

interface LoyaltyCardProps {
params: {
locale: Locale;
};
}

export default async function LoyaltyCard({
params: { locale },
}: LoyaltyCardProps) {
const loyaltyCard = await getLoyaltyCardOrganizer();
return <LoyaltyCardPage loyaltyCard={loyaltyCard} />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ async function ContentSpaceSheetPageContent({
return (
<ContentSpaceSheet
contentSpace={contentSpace}
organizerId={user.role.organizerId}
organizerId={user.role?.organizerId}
/>
);
}
Expand Down
2 changes: 2 additions & 0 deletions apps/back-office/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { NextRequest, NextResponse } from 'next/server';
// TODO adapt this list to roles with restricted access to some routes + update tests
const authPages = [
'user',
'loyalty-card',
'loyalty-card/*',
'campaigns',
'campaigns/*',
'perks',
Expand Down
2 changes: 0 additions & 2 deletions apps/back-office/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,6 @@ const nextConfig = {
'@nft/event-pass',
],
// https://vercel.com/docs/concepts/deployments/skew-protection#enabling-skew-protection
useDeploymentId: true,
useDeploymentIdServerActions: true,
typedRoutes: false, // no solution found to get it working with nx monorepo (not accessible from external libs like feature)
},
sentry: {
Expand Down
3 changes: 1 addition & 2 deletions apps/back-office/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
"defaultConfiguration": "production",
"options": {
"outputPath": "dist/apps/back-office",
"postcssConfig": "apps/back-office/postcss.config.js",
"debug": true
"postcssConfig": "apps/back-office/postcss.config.js"
},
"assets": [
{
Expand Down
1 change: 1 addition & 0 deletions apps/back-office/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"../../dist/apps/back-office/.next/types/**/*.ts"
],
"exclude": [
"**/{web,unlock}/**",
"**/**/examples.tsx",
"node_modules",
"jest.config.ts",
Expand Down
2 changes: 1 addition & 1 deletion apps/back-office/tsconfig.spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"module": "nodenext",
"types": ["jest", "node", "@testing-library/jest-dom", "@next/types"],
"jsx": "react"
},
Expand Down
3 changes: 2 additions & 1 deletion apps/scripts/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const giveRoleForTest = async (
{
applicantId: `fake-${Math.floor(Math.random() * 10000)}`,
externalUserId: account.id,
createDate: new Date(),
reviewStatus: KycStatus_Enum.Completed,
levelName: KycLevelName_Enum.AdvancedKycLevel,
},
Expand All @@ -52,7 +53,7 @@ export const giveRoleForTest = async (

// Assuming the first argument is the address, the second is the role, and the third is the organizerId
console.log(process.argv);
const address = process.argv[2];
const address = process.argv[2].toLowerCase();
const role = process.argv[3] as Roles_Enum;
const organizerId = process.argv[4];

Expand Down
2 changes: 1 addition & 1 deletion apps/scripts/tsconfig.app.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "ESNext",
"module": "nodenext",
"types": ["node"]
},
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
Expand Down
9 changes: 5 additions & 4 deletions apps/scripts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext"
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.app.json"
}
],
"compilerOptions": {
"esModuleInterop": true
}
]
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {
OffKeyHeaderConnected,
OffKeyProfile,
OffKeyViewHeaderConnected,
} from '@features/unlock/shopify';
import { messages, type Locale } from '@next/i18n';
import { deepPick } from '@utils';
import { NextIntlClientProvider } from 'next-intl';
import dynamic from 'next/dynamic';

interface HeaderProps {
params: {
Expand All @@ -15,6 +15,11 @@ interface HeaderProps {
};
}

const OffKeyProfile = dynamic(
async () => (await import('@features/unlock/shopify')).OffKeyProfile,
{ ssr: false },
);

export default function Header({
params: { locale, gateId, address },
}: HeaderProps) {
Expand Down
14 changes: 7 additions & 7 deletions apps/unlock/app/[locale]/shopify/[gateId]/[address]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { OffKeyGate, OffKeyState } from '@features/unlock/shopify';
import { messages, type Locale } from '@next/i18n';
import { deepPick } from '@utils';
import { NextIntlClientProvider } from 'next-intl';
import dynamic from 'next/dynamic';

interface GateProps {
params: {
Expand All @@ -11,6 +11,11 @@ interface GateProps {
};
}

const OffKeyGate = dynamic(
async () => (await import('@features/unlock/shopify')).OffKeyGate,
{ ssr: false },
);

export default function Gate({
params: { locale, gateId, address },
}: GateProps) {
Expand All @@ -20,12 +25,7 @@ export default function Gate({
]);
return (
<NextIntlClientProvider locale={locale} messages={localeMessages}>
<OffKeyGate
className="flex-1 pt-2"
gateId={gateId}
address={address}
initialGateState={OffKeyState.Unlocked}
/>
<OffKeyGate className="flex-1 pt-2" gateId={gateId} address={address} />
</NextIntlClientProvider>
);
}
3 changes: 0 additions & 3 deletions apps/unlock/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,6 @@ const nextConfig = {
'@nft/thirdweb-organizer',
'@nft/event-pass',
],
// https://vercel.com/docs/concepts/deployments/skew-protection#enabling-skew-protection
useDeploymentId: true,
useDeploymentIdServerActions: true,
typedRoutes: false, // no solution found to get it working with nx monorepo (not accessible from external libs like feature)
},
sentry: {
Expand Down
3 changes: 1 addition & 2 deletions apps/unlock/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
"defaultConfiguration": "production",
"options": {
"outputPath": "dist/apps/unlock",
"postcssConfig": "apps/unlock/postcss.config.js",
"debug": true
"postcssConfig": "apps/unlock/postcss.config.js"
},
"assets": [
{
Expand Down
1 change: 1 addition & 0 deletions apps/unlock/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"../../dist/apps/unlock/.next/types/**/*.ts"
],
"exclude": [
"**/{web,back-office}/**",
"node_modules",
"**/**/examples.tsx",
"jest.config.ts",
Expand Down
2 changes: 1 addition & 1 deletion apps/unlock/tsconfig.spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"module": "nodenext",
"types": ["jest", "node", "@testing-library/jest-dom", "@next/types"],
"jsx": "react"
},
Expand Down
3 changes: 2 additions & 1 deletion apps/web/app/[locale]/cart/purchase/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { isUserKycValidated } from '@kyc/common';
import { redirect } from '@next/navigation';
import { getCurrentUser } from '@next/next-auth/user';
import { redirect as nextRedirect } from 'next/navigation';
import { getErrorMessage } from '@utils';

interface CartSectionProps {
params: {
Expand All @@ -25,7 +26,7 @@ export default async function CartPurchase({
try {
session = await getStripeActiveCheckoutSession();
} catch (error) {
if (error.message === 'User has no email') {
if (getErrorMessage(error) === 'User has no email') {
return redirect('/cart?reason=no-mail');
}
}
Expand Down
43 changes: 43 additions & 0 deletions apps/web/app/api/shopify/admin/_middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import env from '@env/server';
import jwt from 'jsonwebtoken';
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';

export const config = {
matcher: '/api/shopify/admin/:path*',
};

// WIP: Complete the implementation of Shopify admin API middleware.
// This includes verifying session tokens, errors and test more thoughtfully with shopify app making requests using APP Bridge
export async function middleware(request: NextRequest) {
const sessionToken = request.headers
.get('authorization')
?.split('Bearer ')[1];
if (!sessionToken) {
return new Response('Unauthorized: No session token provided', {
status: 401,
});
}

try {
await new Promise((resolve, reject) => {
jwt.verify(
sessionToken,
env.SHOPIFY_SHARED_SECRET,
{ algorithms: ['HS256'] },
(err, decoded) => {
if (err) {
reject(err);
} else {
resolve(decoded);
}
},
);
});

return NextResponse.next();
} catch (error) {
console.error('Token verification error:', error);
return new Response('Unauthorized: Invalid token', { status: 401 });
}
}
21 changes: 21 additions & 0 deletions apps/web/app/api/shopify/loyalty-card/[contractAddress]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ShopifyWebhookAndApiHandler } from '@integrations/external-api-handlers';
import { NextRequest } from 'next/server';

export async function POST(
req: NextRequest,
{ params: { contractAddress } }: { params: { contractAddress: string } },
) {
const shopifyHandler = new ShopifyWebhookAndApiHandler();
return shopifyHandler.mintLoyaltyCardWithCustomerId({
req,
contractAddress,
});
}

export async function GET(
req: NextRequest,
{ params: { contractAddress } }: { params: { contractAddress: string } },
) {
const shopifyHandler = new ShopifyWebhookAndApiHandler();
return shopifyHandler.hasLoyaltyCard({ req, contractAddress });
}
Loading
Loading