diff --git a/.env.example b/.env.example index 75d836df..28bd7471 100644 --- a/.env.example +++ b/.env.example @@ -18,3 +18,9 @@ POSTGRES_DATABASE="" # The URL of the Sismo API Hub NEXT_PUBLIC_HUB_API_URL="https://hub.sismo.io" + +# Defender ZK Badge relayer +SH_RELAY_DEFENDER_API_KEYS={"mumbai": { "key": "", "secret": ""}} + +# Zk Badge contract addresses +NEXT_PUBLIC_ZK_BADGE_ADDRESSES={ "mumbai": "", "sepolia": ""} \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index 7e1cf5e6..bbc7aba5 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,14 @@ { "tabWidth": 2, - "printWidth": 100 -} + "printWidth": 100, + + "overrides": [ + { + "files": "*.sol", + "options": { + "tabWidth": 2, + "printWidth": 100 + } + } + ] +} \ No newline at end of file diff --git a/jest.setup.js b/jest.setup.js index bdc69d83..e72c83eb 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -3,4 +3,7 @@ // Used for __tests__/testing-library.js // Learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom/extend-expect' \ No newline at end of file +import '@testing-library/jest-dom/extend-expect' +const { TextEncoder } = require('text-encoding'); + +global.TextEncoder = TextEncoder; \ No newline at end of file diff --git a/next.config.js b/next.config.js index 1929f634..f89924d9 100644 --- a/next.config.js +++ b/next.config.js @@ -9,19 +9,14 @@ const nextConfig = { sassOptions: { additionalData: `@use "styles/color" as color; @use "styles/mixin" as mixin;`, includePaths: [path.join(__dirname, 'src')], - }, - compiler: { - styledComponents: true -} -// redirects: async () => { -// return [ -// { -// source: "/the-merge-contributors", -// destination: "/space/the-merge-contributors", -// permanent: true -// }, -// ] -// } + }, + compiler: { + styledComponents: true + }, + webpack: config => { + config.resolve.fallback = { fs: false, net: false, tls: false }; + return config; + }, } module.exports = nextConfig \ No newline at end of file diff --git a/package.json b/package.json index 01429296..49679549 100644 --- a/package.json +++ b/package.json @@ -16,17 +16,22 @@ }, "dependencies": { "@google-cloud/local-auth": "^2.1.1", + "@rainbow-me/rainbowkit": "^1.0.5", "@sismo-core/sismo-connect-react": "^0.0.16", "@sismo-core/sismo-connect-server": "^0.0.16", "@types/luxon": "^3.3.0", "@types/node": "18.16.1", "@types/react": "18.2.0", "@types/react-dom": "18.2.1", + "aws-sdk": "^2.1411.0", "axios": "^1.4.0", "bufferutil": "^4.0.7", "classnames": "^2.3.2", + "defender-autotask-utils": "^1.44.0", + "defender-relay-client": "^1.44.0", "eslint": "8.39.0", "eslint-config-next": "13.3.1", + "ethers": "5.6.2", "google-auth-library": "^8.7.0", "googleapis": "^118.0.0", "luxon": "^3.3.0", @@ -40,9 +45,12 @@ "server-only": "^0.0.1", "sharp": "^0.32.1", "styled-components": "^6.0.0-beta.15", + "text-encoding": "^0.7.0", "typeorm": "^0.3.17", "typescript": "5.0.4", - "utf-8-validate": "^6.0.3" + "utf-8-validate": "^6.0.3", + "viem": "^1.2.12", + "wagmi": "^1.3.7" }, "devDependencies": { "@testing-library/jest-dom": "5.16.4", diff --git a/space-configs/main/sismo.ts b/space-configs/main/sismo.ts index 69728ba6..908c2633 100644 --- a/space-configs/main/sismo.ts +++ b/space-configs/main/sismo.ts @@ -37,7 +37,6 @@ export default { "Future of France is an invitation-only event during EthCC week in Paris, organized by French-based crypto startups. The number of tickets is limited. Exclusive for members of Sismo Community Level 3.", tags: ["Event", "Ticket"], image: "sismo_appstore_fof_tickets.png", - createdAt: new Date("2023-07-03T18:00"), }, sismoConnectRequest: { diff --git a/space-configs/types.ts b/space-configs/types.ts index 0c756e87..38d8b95b 100644 --- a/space-configs/types.ts +++ b/space-configs/types.ts @@ -1,3 +1,4 @@ +import { Network } from "@/src/libs/contracts/networks"; import { AuthRequest, ClaimRequest } from "@sismo-core/sismo-connect-react"; export type SpaceConfig = { @@ -53,12 +54,11 @@ type AppCommonConfig = { }; }; -export type ExternalAppTemplateConfig = { - link: string; -}; export type ExternalAppConfig = AppCommonConfig & { type: "external"; - templateConfig: ExternalAppTemplateConfig; + templateConfig: { + link: string; + }; }; export type UserSelection = FirstComeFirstServed | Lottery; @@ -86,14 +86,25 @@ export type ZkDropAppConfig = AppCommonConfig & { }; }; +export type ZkBadgeChainName = Network.Gnosis | Network.Mumbai | Network.Sepolia; export type ZkBadgeAppConfig = AppCommonConfig & { - type: "zkbadge"; + type: "zkBadge"; templateConfig: { step1CtaText?: string; step2CtaText: string; appDescription?: string; - chainId: number; - collectionId: string; + tokenId: string; + badgeMetadata: { + name: string; + description: string; + image: string; + }; + chains: [ + { + name: ZkBadgeChainName; + relayerEnabled?: boolean; + } + ]; }; }; diff --git a/src/app/(home)/page.tsx b/src/app/(home)/page.tsx index 2b74ed87..7ff07d62 100644 --- a/src/app/(home)/page.tsx +++ b/src/app/(home)/page.tsx @@ -1,7 +1,8 @@ -import { getApps, getSpaces } from "../../libs/spaces/spaces"; -import { SpaceType, ZkAppType } from "../../libs/spaces"; + +import { SpaceType, ZkAppType } from "../../services/spaces-service"; import HomeMain from "@/src/components/HomeMain"; import env from "@/src/environments"; +import ServiceFactory from "@/src/services/service-factory/service-factory"; export type SpaceImportedImage = { config: SpaceType; @@ -38,9 +39,10 @@ export async function generateMetadata() { } export default async function HomePage() { - const spaces: SpaceType[] = await getSpaces(); - const apps: ZkAppType[] = await getApps({ sortedBy: "createdAt" }); + const spacesService = ServiceFactory.getSpacesService(); + const spaces: SpaceType[] = await spacesService.getSpaces(); + const apps: ZkAppType[] = await spacesService.getApps({ sortedBy: "createdAt" }); return <>{spaces && }; } diff --git a/src/app/[...app]/page.tsx b/src/app/[...app]/page.tsx index 4b92def4..77bea4c6 100644 --- a/src/app/[...app]/page.tsx +++ b/src/app/[...app]/page.tsx @@ -2,13 +2,16 @@ import 'server-only'; import env from "@/src/environments"; import { ClaimRequest } from "@sismo-core/sismo-connect-server"; import { notFound } from "next/navigation"; -import { ZkAppType, getApps, getSpaces } from "@/src/libs/spaces"; +import { ZkAppType } from "@/src/services/spaces-service"; import { GroupProvider, GroupSnapshotMetadata } from "@/src/libs/group-provider"; import AppMain from "@/src/components/AppMain"; +import ServiceFactory from '@/src/services/service-factory/service-factory'; // This function runs at build time on the server it generates the static paths for each page export async function generateStaticParams() { - const spaces = await getSpaces(); + const spacesService = ServiceFactory.getSpacesService(); + + const spaces = await spacesService.getSpaces(); type ZkAppTypeWithSpaceSlug = ZkAppType & { spaceSlug: string; }; @@ -37,11 +40,13 @@ export async function generateStaticParams() { // This function runs at build time on the server it generates the HTML metadata for each page export async function generateMetadata({ params }: { params: { app: [string, string] } }) { + const spacesService = ServiceFactory.getSpacesService(); + let app: ZkAppType; let appImage; try { const { app: slug } = params; - const apps = await getApps({ where: { spaceSlug: slug[0], appSlug: slug[1] } }); + const apps = await spacesService.getApps({ where: { spaceSlug: slug[0], appSlug: slug[1] } }); if (apps.length !== 1) return notFound(); app = apps[0]; @@ -78,9 +83,11 @@ export async function generateMetadata({ params }: { params: { app: [string, str // This function runs at build time on the server it generates the HTML for each page export default async function AppPage({ params }: { params: { app: [string, string] } }) { + const spacesService = ServiceFactory.getSpacesService(); + const { app: slug } = params; - const apps = await getApps({ where: { spaceSlug: slug[0], appSlug: slug[1] } }); + const apps = await spacesService.getApps({ where: { spaceSlug: slug[0], appSlug: slug[1] } }); if (apps.length !== 1) return notFound(); const app = apps[0]; diff --git a/src/app/[space]/page.tsx b/src/app/[space]/page.tsx index 2159b539..1c57388b 100644 --- a/src/app/[space]/page.tsx +++ b/src/app/[space]/page.tsx @@ -1,11 +1,14 @@ import getImgSrcFromConfig, { ImportedNextImage } from "@/src/utils/getImgSrcFromConfig"; import { notFound } from "next/navigation"; import SpacesMain from "@/src/components/SpacesMain"; -import { SpaceType, ZkAppType, getSpace, getSpaces } from "@/src/libs/spaces"; +import { SpaceType, ZkAppType } from "@/src/services/spaces-service"; +import ServiceFactory from "@/src/services/service-factory/service-factory"; // This function runs at build time on the server it generates the static paths for each page export async function generateStaticParams() { - const spaces = await getSpaces(); + const spacesService = ServiceFactory.getSpacesService(); + + const spaces = await spacesService.getSpaces(); return spaces?.map((space: SpaceType) => { return { space: space.slug, @@ -15,11 +18,17 @@ export async function generateStaticParams() { // This function runs at build time on the server it generates the HTML metadata for each page export async function generateMetadata({ params }: { params: { space: string } }) { + const spacesService = ServiceFactory.getSpacesService(); + let space: SpaceType; let imageMetadata: string; try { const { space: slug } = params; - space = await getSpace({ slug: slug }); + const spaces = await spacesService.getSpaces({ where: { spaceSlug: slug } }); + if (!spaces || spaces.length !== 1) { + notFound(); + } + space = spaces[0]; let pfpImage; if (typeof space?.profileImage === "string") { pfpImage = await getImgSrcFromConfig({ @@ -68,11 +77,16 @@ export type ImportedImage = { // This function runs at build time on the server it generates the HTML for each page export default async function SpacePage({ params }: { params: { space: string } }) { + const spacesService = ServiceFactory.getSpacesService(); + let space: SpaceType; try { const { space: slug } = params; - const _space = await getSpace({ slug: slug }); - space = _space; + const _spaces = await spacesService.getSpaces({ where: { spaceSlug: slug } }); + if (!_spaces || _spaces?.length !== 1) { + notFound(); + } + space = _spaces[0]; } catch (e) { notFound(); } diff --git a/src/app/api/zk-badge/image/[tokenId]/route.test.ts b/src/app/api/zk-badge/image/[tokenId]/route.test.ts new file mode 100644 index 00000000..3d67eb71 --- /dev/null +++ b/src/app/api/zk-badge/image/[tokenId]/route.test.ts @@ -0,0 +1,67 @@ +/** + * @jest-environment node + */ +import ServiceFactory from "@/src/services/service-factory/service-factory"; +import { GET } from "../../image/[tokenId]/route"; +import { spaceMock1, spaceMock2 } from "@/src/services/spaces-service/tests/spaces-mock"; +import fs from 'fs'; +import path from 'path'; + + +describe('GET /api/zk-badge/image/[tokenId]', () => { + + beforeEach(() => { + let spacesService = ServiceFactory.getSpacesService(); + const configs = [ + spaceMock1, + spaceMock2 + ] + spacesService.updateConfigs(configs); + }) + + afterEach(() => { + let configs = ServiceFactory.getSpaceConfigs(); + let spacesService = ServiceFactory.getSpacesService(); + spacesService.updateConfigs(configs); + jest.clearAllMocks(); + }) + + it('should return an error response if no badge is found', async () => { + const params = { tokenId: "1234" }; + const response = await GET(null, { params }); + const data = await response.json(); + expect(data).toEqual({ error: 'No badge found for tokenId: 1234' }); + }); + + it('should return an error response if no badge image is found', async () => { + const params = { tokenId: "40000001" }; + const response = await GET(null, { params }); + const data = await response.json(); + expect(data).toEqual({ error: 'No badge image found for tokenId: 40000001' }); + }); + + it('should return an image response if a badge is found', async () => { + jest.spyOn(fs, 'readFileSync'); + + const params = { tokenId: "40000001" }; + const dummyImageBuffer = Buffer.from([0, 1, 2, 3, 4, 5]); + const imagePath = path.join(process.cwd(), '/space-config/space/images/image.png'); + + (fs.readFileSync as jest.Mock).mockImplementation((path) => { + if (path === imagePath) { + return dummyImageBuffer; + } + }); + + const response = await GET(null, { params }); + + const reader = response.body.getReader(); + const result = await reader.read(); + + const data = Buffer.from(result.value); + + expect(Buffer.compare(data, dummyImageBuffer)).toEqual(0); + expect(response.status).toEqual(200); + expect(response.headers.get('Content-Type')).toEqual('image/jpeg'); + }); +}); \ No newline at end of file diff --git a/src/app/api/zk-badge/image/[tokenId]/route.ts b/src/app/api/zk-badge/image/[tokenId]/route.ts new file mode 100644 index 00000000..cc635f9c --- /dev/null +++ b/src/app/api/zk-badge/image/[tokenId]/route.ts @@ -0,0 +1,41 @@ +import ServiceFactory from "@/src/services/service-factory/service-factory"; +import { ZkBadgeAppType } from "@/src/services/spaces-service"; +import fs from 'fs'; +import { NextResponse } from "next/server"; +import path from 'path'; + +export async function GET(req: Request, { params }: { params: { tokenId: string } }) { + const tokenId = params.tokenId; + const spacesService = ServiceFactory.getSpacesService(); + let apps = await spacesService.getApps(); + apps = apps.filter(app => app.type === "zkBadge") ; + const badge = (apps as ZkBadgeAppType[]).find(app => app.tokenId === tokenId); + if (!badge) { + return NextResponse.json({ + error: `No badge found for tokenId: ${tokenId}` + }) + } + + let readableStream; + try { + const imagePath = path.join(process.cwd(), `/space-config/${badge.space.slug}/images/${badge.badgeMetadata.image}`); + const file = fs.readFileSync(imagePath); + readableStream = new ReadableStream({ + start(controller) { + controller.enqueue(new Uint8Array(file)); + controller.close(); + } + }); + } catch (e) { + return NextResponse.json({ + error: `No badge image found for tokenId: ${tokenId}` + }) + } + + return new NextResponse(readableStream, { + status: 200, + headers: { + 'Content-Type': 'image/jpeg' + }, + }) +} \ No newline at end of file diff --git a/src/app/api/zk-badge/metadata/[id]/route.test.ts b/src/app/api/zk-badge/metadata/[id]/route.test.ts new file mode 100644 index 00000000..cc8f5651 --- /dev/null +++ b/src/app/api/zk-badge/metadata/[id]/route.test.ts @@ -0,0 +1,48 @@ +/** + * @jest-environment node + */ +import ServiceFactory from "@/src/services/service-factory/service-factory"; +import { decodeCollectionId } from "@/src/utils/collectionId"; +import { spaceMock1, spaceMock2 } from "@/src/services/spaces-service/tests/spaces-mock"; +import { GET } from "./route"; + +describe('GET /api/zk-badge/metadata/[i]', () => { + + beforeEach(() => { + let spacesService = ServiceFactory.getSpacesService(); + const configs = [ + spaceMock1, + spaceMock2 + ] + spacesService.updateConfigs(configs); + }) + + afterEach(() => { + let configs = ServiceFactory.getSpaceConfigs(); + let spacesService = ServiceFactory.getSpacesService(); + spacesService.updateConfigs(configs); + }) + + it('should return an error response if no badge is found', async () => { + const req = new Request('https://example.com'); + const id = decodeCollectionId("1234"); + const params = { id: id + ".json" }; + const response = await GET(req, { params }); + const data = await response.json(); + expect(data).toEqual({ error: 'No badge found for id: 1234' }); + }); + + it('should return a badge if found', async () => { + const req = new Request('https://example.com'); + const id = decodeCollectionId("40000001"); + const params = { id: id.toString() }; + + const response = await GET(req, { params }); + const data = await response.json(); + expect(data).toEqual({ + name: "Badge name", + description: "Badge description", + image: `https://example.com/api/zk-badge/image/40000001`, + }); + }); +}); \ No newline at end of file diff --git a/src/app/api/zk-badge/metadata/[id]/route.ts b/src/app/api/zk-badge/metadata/[id]/route.ts new file mode 100644 index 00000000..a7ba38ec --- /dev/null +++ b/src/app/api/zk-badge/metadata/[id]/route.ts @@ -0,0 +1,24 @@ +import ServiceFactory from "@/src/services/service-factory/service-factory"; +import { ZkBadgeAppType } from "@/src/services/spaces-service"; +import { encodeCollectionId } from "@/src/utils/collectionId"; +import { NextResponse } from "next/server"; + + +export async function GET(req: Request, { params }: { params: { id: string } }) { + const url = new URL(req.url); + const id = parseInt(params.id.split(".")[0]); + const spacesService = ServiceFactory.getSpacesService(); + let apps = await spacesService.getApps(); + apps = apps.filter(app => app.type === "zkBadge") ; + const badge = (apps as ZkBadgeAppType[]).find(app => parseInt(app.tokenId) === parseInt(encodeCollectionId(id))); + if (!badge) { + return NextResponse.json({ + error: `No badge found for id: ${parseInt(encodeCollectionId(id))}` + }) + } + return NextResponse.json({ + name: badge.badgeMetadata.name, + description: badge.badgeMetadata.description, + image: url.protocol + "//" + url.hostname + (url.port ? ":" + url.port : "") + "/api/zk-badge/image/" + badge.tokenId + }) +} \ No newline at end of file diff --git a/src/app/api/zk-badge/relay-tx/route.test.ts b/src/app/api/zk-badge/relay-tx/route.test.ts new file mode 100644 index 00000000..9c60c450 --- /dev/null +++ b/src/app/api/zk-badge/relay-tx/route.test.ts @@ -0,0 +1,48 @@ +/** + * @jest-environment node + */ +import { POST } from "./route"; + + +const mockMint = jest.fn().mockImplementation(() => Promise.resolve({ hash: 'mockTxHash' })); + +jest.mock("../../../../libs/contracts/signers", () => { + return { + getDefenderRelayerSigner: jest.fn().mockReturnValue({}), + }; +}); + +jest.mock('../../../../libs/contracts/zk-badge', () => { + // Use the 'mockImplementation' method to mock the 'ZkBadgeContract' class + const originalModule = jest.requireActual("../../../../libs/contracts/zk-badge"); + + return { + __esModule: true, + ...originalModule, + ZkBadgeContract: jest.fn().mockImplementation(() => { + return { + mint: mockMint, + }; + }), + }; +}); + +describe('POST /api/zk-badge/relay-tx', () => { + it('calls mint with the correct arguments', async () => { + const req: any = { + json: jest.fn().mockResolvedValue({ + responseBytes: 'mockResponseBytes', + destination: 'mockDestination', + tokenId: 'mockTokenId' + }) + }; + + await POST(req); + + expect(mockMint).toHaveBeenCalledWith({ + responseBytes: 'mockResponseBytes', + address: 'mockDestination', + tokenId: 'mockTokenId' + }); + }); +}); \ No newline at end of file diff --git a/src/app/api/zk-badge/relay-tx/route.ts b/src/app/api/zk-badge/relay-tx/route.ts new file mode 100644 index 00000000..4c9a9dd1 --- /dev/null +++ b/src/app/api/zk-badge/relay-tx/route.ts @@ -0,0 +1,29 @@ +import { Network } from "@/src/libs/contracts/networks"; +import { getDefenderRelayerSigner } from "@/src/libs/contracts/signers"; +import { ZkBadgeContract } from "@/src/libs/contracts/zk-badge"; +import { NextResponse } from "next/server"; + +export async function POST(req: Request) { + const { responseBytes, destination, tokenId, chain } = await req.json(); + + const signer = getDefenderRelayerSigner(Network.Mumbai); + const zkMinterContract = new ZkBadgeContract({ network: chain, signer }); + + try { + const tx = await zkMinterContract.mint({ + responseBytes, + address: destination, + tokenId + }); + + return NextResponse.json({ + success: true, + txHash: tx.hash + }); + } catch (e) { + console.error(e); + return NextResponse.json({ + code: "minting-error" + }) + } +} \ No newline at end of file diff --git a/src/app/api/zk-form/verify/route.ts b/src/app/api/zk-form/verify/route.ts index c1b8789b..e7088a0b 100644 --- a/src/app/api/zk-form/verify/route.ts +++ b/src/app/api/zk-form/verify/route.ts @@ -10,9 +10,8 @@ import { } from "@sismo-core/sismo-connect-server"; import { NextResponse } from "next/server"; import { mapAuthTypeToSheetColumnName } from "@/src/utils/mapAuthTypeToSheetColumnName"; -import { getApps } from "@/src/libs/spaces"; import { getImpersonateAddresses } from "@/src/utils/getImpersonateAddresses"; -import { ZkAppType, ZkFormAppType } from "@/src/libs/spaces/types"; +import { ZkAppType, ZkFormAppType } from "@/src/services/spaces-service/types"; import ServiceFactory from "@/src/services/service-factory/service-factory"; import { errorResponse } from "@/src/libs/helper/api"; import { isClaimEquals } from "@/src/app/api/zk-form/verify/helper"; @@ -26,7 +25,9 @@ export type Field = { export async function POST(req: Request) { const { fields, response, spaceSlug, appSlug } = await req.json(); const store = ServiceFactory.getZkFormTableStore(); - const apps = await getApps({ where: { appSlug: appSlug, spaceSlug: spaceSlug }}); + const spacesService = ServiceFactory.getSpacesService(); + + const apps = await spacesService.getApps({ where: { appSlug: appSlug, spaceSlug: spaceSlug }}); if (!apps || apps.length !== 1 || apps[0].type !== "zkForm") { return errorResponse(`App ${appSlug} not found or not a zkForm app`); diff --git a/src/app/api/zk-telegram-bot/verify/route.ts b/src/app/api/zk-telegram-bot/verify/route.ts index 79304bf6..066756e4 100644 --- a/src/app/api/zk-telegram-bot/verify/route.ts +++ b/src/app/api/zk-telegram-bot/verify/route.ts @@ -1,5 +1,5 @@ import { NextResponse } from "next/server"; -import { ZkAppType, ZkTelegramBotAppType, getApps, getSpace } from "@/src/libs/spaces"; +import { ZkAppType, ZkTelegramBotAppType } from "@/src/services/spaces-service"; import { AuthType, SismoConnect, @@ -14,10 +14,13 @@ import { errorResponse } from "@/src/libs/helper/api"; export async function POST(req: Request) { const logger = ServiceFactory.getLoggerService(); const userStore = ServiceFactory.getZkTelegramBotUserStore(); + const spacesService = ServiceFactory.getSpacesService(); + const { response, spaceSlug, appSlug } = await req.json(); - const space = await getSpace({ slug: spaceSlug }); - const apps = await getApps({ where: { appSlug: appSlug, spaceSlug: space.slug } }); + const spaces = await spacesService.getSpaces({ where: { spaceSlug: spaceSlug } }); + const apps = await spacesService.getApps({ where: { appSlug: appSlug, spaceSlug: spaces[0].slug }}); + if (!apps || apps.length !== 1) { return errorResponse(`Failed to find app ${appSlug} in space ${spaceSlug}`); } diff --git a/src/app/api/zk-telegram-bot/webhook/route.ts b/src/app/api/zk-telegram-bot/webhook/route.ts index d1a1ce7b..1a7d6112 100644 --- a/src/app/api/zk-telegram-bot/webhook/route.ts +++ b/src/app/api/zk-telegram-bot/webhook/route.ts @@ -1,5 +1,5 @@ import { NextResponse } from "next/server"; -import { ZkTelegramBotAppType, getSpaces } from "@/src/libs/spaces"; +import { ZkTelegramBotAppType } from "@/src/services/spaces-service"; import env from "@/src/environments"; import ServiceFactory from "@/src/services/service-factory/service-factory"; @@ -56,7 +56,8 @@ export async function POST(request: Request) { } const findApp = async (groupId: string): Promise => { - const spaces = await getSpaces(); + const spacesService = ServiceFactory.getSpacesService(); + const spaces = await spacesService.getSpaces(); for (let space of spaces) { for (let app of space.apps) { if (app.type === "zkTelegramBot") { diff --git a/src/app/explore/apps/page.tsx b/src/app/explore/apps/page.tsx index 314a2db2..399b84d2 100644 --- a/src/app/explore/apps/page.tsx +++ b/src/app/explore/apps/page.tsx @@ -1,6 +1,7 @@ import { SpaceConfig } from "@/space-configs/types"; import ExploreAppsMain from "@/src/components/ExploreAppsMain"; -import { ZkAppType, getApps } from "@/src/libs/spaces"; +import ServiceFactory from "@/src/services/service-factory/service-factory"; +import { ZkAppType } from "@/src/services/spaces-service"; export type SpaceImportedImage = { config: SpaceConfig; @@ -8,7 +9,9 @@ export type SpaceImportedImage = { }; export default async function ExplorePage() { - const apps: ZkAppType[] = await getApps({ sortedBy: "createdAt" }); + const spacesService = ServiceFactory.getSpacesService(); + + const apps: ZkAppType[] = await spacesService.getApps({ sortedBy: "createdAt" }); return ( diff --git a/src/app/explore/spaces/page.tsx b/src/app/explore/spaces/page.tsx index 414f8540..2a4a6699 100644 --- a/src/app/explore/spaces/page.tsx +++ b/src/app/explore/spaces/page.tsx @@ -1,6 +1,7 @@ import { SpaceConfig } from "@/space-configs/types"; import ExploreSpacesMain from "@/src/components/ExploreSpacesMain"; -import { SpaceType, getSpaces } from "@/src/libs/spaces"; +import ServiceFactory from "@/src/services/service-factory/service-factory"; +import { SpaceType } from "@/src/services/spaces-service"; export type SpaceImportedImage = { config: SpaceConfig; @@ -8,7 +9,9 @@ export type SpaceImportedImage = { }; export default async function ExplorePage() { - const spaces: SpaceType[] = await getSpaces(); + const spacesService = ServiceFactory.getSpacesService(); + + const spaces: SpaceType[] = await spacesService.getSpaces(); return ( diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 0143f0bd..ef413641 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -7,6 +7,7 @@ import { PageContent } from "../components/Layouts/PageContent"; import PageContainer from "../components/Layouts/PageContainer"; import PlausibleProvider from "next-plausible"; import env from "../environments"; +import AppProviders from "../components/AppProviders"; export const metadata = { title: env.isDemo @@ -28,11 +29,13 @@ export default function RootLayout({ children }: { children: React.ReactNode }) - - - {children} -