[!IMPORTANT]
Offline development has been stopped on July 2024 and the project is no longer maintained.
Overview • Architecture • Quick Install • Environment Configuration • Project Structure • Tech Stack • Libraries • Tests • NX Workspace • Troubleshoot • Services • License
Offline is an innovative platform designed to revolutionize brand-customer interactions through user-centric applications leveraging blockchain and NFT technologies. The project consists of three main applications:
Marketplace (apps/web
): The user-facing app where customers can browse and purchase tickets for events offered by organizers. This serves as the main entry point for consumers to interact with brands and access exclusive experiences.
Back-office (apps/back-office
): A comprehensive management interface for organizers to create and manage their events, smart contracts, and analyze performance metrics. This empowers brands to easily create and manage their offerings on the Offline platform.
Unlock (apps/unlock
): An iframe app that allows users to connect their Offline smart contract wallet across various platforms outside of Offline. This includes integration with Shopify stores for token-gating campaigns, expanding the utility of Offline's blockchain-based assets.
The Offline Marketplace Monorepo employs a microservices architecture with the following key components:
Hasura: Serves as the central data layer, providing a GraphQL API for all three Next.js applications. This ensures consistent data access and management across the platform.
Next.js Apps:
apps/web
)apps/back-office
)apps/unlock
)Each app is built using Next.js 14 with the App Router, ensuring optimal performance and SEO capabilities.
Blockchain Integration: Utilizes smart contracts for ticket management and token-gating functionality. This provides a secure and transparent way to manage digital assets and access rights.
External Integrations: Includes a Shopify app (separate project: shopify-gates) for creating token-gating campaigns on merchant stores, powered by OF Keys and OF Stamps. This extends the reach of Offline's technology to e-commerce platforms.
npm install -g pnpm
pnpm install
.env.local
file.[!TIP] Refer to the Environment Configuration section to set up your
.env.local
with the required API keys.
pnpm start
[!NOTE]
In order to run the project, you need to configure the following environment variables in you.env.local
file
Our platform uses Alchemy as an RPC provider for the Polygon blockchain. You need to create an account and get an API key on the alchemy dashboard:
NEXT_PUBLIC_ALCHEMY_API_KEY=
ALCHEMY_API_KEY=
ALCHEMY_AUTH_TOKEN=
# Warning ! Those api keys are going to get leaked in the client side code so it's advised to set ALLOWLIST DOMAIN in the alchemy dashboard to your apex domain (in our case www.offline.live) in order to avoid someone hijacking your api keys.
In order to secure your JWT authentication provided by Next Auth you are going to need to generate your own RSA-256 keys.
You need to configure hasura and next auth to have the same asymmetric key. One is provided by default but you can generate your own RSA 256 key using those commands:
# Don't add passphrase
ssh-keygen -t rsa -P "" -b 4096 -m PEM -f jwtRS256.key
ssh-keygen -e -m PEM -f jwtRS256.key > jwtRS256.key.pub
https://hasura.io/blog/next-js-jwt-authentication-with-next-auth-and-integration-with-hasura/
awk -v ORS='\\n' '1' jwtRS256.key.pub | pbcopy
HASURA_GRAPHQL_JWT_SECRET
env in the format{ "type": "RS256", "key": "<insert-your-public-key-here>"}
NEXTAUTH_SECRET
envcat jwtRS256.key | pbcopy
Don't forget to add double quotes "" arround so that \n
are interpreted correctly
The console is used as a backoffice to handle the graphQL server and to innerlink all the microservices.
This is the main web app where you can browse and purchase tickets for events. It also serve as an API provider for external integrations like our Shopify tokengating app.
This is the back-office app where organizers can manage their events, smart contracts and analyze the performance metrics.
This is the iframe app where the users connect their smart wallet to our platform seamlessly across domains. It is embedded in the Shopify stores for tokengating campaigns.
This repo is configured to be built with Docker, and Docker compose.
To build all apps in this repo:
pnpm docker:build
To shutdown all running containers:
pnpm docker:stop
To launch all the services containers:
pnpm docker:services
The command to run all the services in this repo is
pnpm docker:services
The command to run all the containers for unit and integration test is
pnpm docker:test
We have four separate Storybooks for different parts of our project:
UI Components: Individual stories for each component (mostly based on shadcn/ui). View UI Storybook
Web Platform: Exposes the Offline platform user-facing app. View Web Storybook
Back-office: Exposes the Offline platform organizer app. View Back-office Storybook
Unlock: Exposes the Offline iframe app. View Unlock Storybook
We use interaction testing with the Storybook version of Jest and Testing Library to provide dynamic demonstrations of individual components along with testing.
Additionally, we use Chromatic in our CI pipeline to spot and approve/decline UI changes across all our Storybooks.
This repo has some additional tools already setup for you:
This project uses Next-Auth in conjunction with Cometh Connect to provide secure authentication for users.
Our authentication method utilizes smart wallet signatures, offering a seamless and secure connection through the verification of a signature on a smart contract. This approach leverages blockchain technology to provide a robust and user-friendly authentication experience.
The authentication providers and configuration can be found in libs/next/next-auth/options
.
We associate the smart account with a Hasura account by persisting the wallet address and a UUID in our database.
For more details on the signature verification process, refer to the Cometh Connect documentation.
The command pnpm graphql-codegen
will launch the graphql-codegen
script. All the codegen definitions are written in the file codegen.ts
. You should run this command each time you modify a graphql query or update something on the hasura console to have the updated generated sdk and utilities functions.
The generator is divided in three parts, corresponding to the role of anonymous
, user
and admin
, targeting the graphql hasura server for those respective roles.
Each one have a graphql schema and an ast schema generated and specfic sdk.
Anonymous
The graphql queries definition are defined in libs/gql/anonymous/api/queries
. We use a generic sdk with a simple fetch query in order to facilitate the querying the data for the anonymous role. Those queries doesn't need any authentication and thus are limited to read only access on some basic information about the events.
User
The graphql queries definition are defined in libs/gql/user/api/queries
. We use the React-Query module in order to facilitate the querying the data for the user role in the fontend client. The hasura service will read the auth cookie in order to validate the request. We also generate a generic sdk in order to facilitate testing of user query with jest on libs/test-utils/gql/src/generated/test-account.ts
where we provide a Bearer JWT instead of a cookie because jest is not capable to provide one.
Admin
The graphql queries definition are defined in libs/gql/admin/api/queries
. We use a generic sdk with a simple fetch query in order to facilitate the querying the data for the admin role. Those queries are made on the server side of the frontend. Hasura will allow the request through the providing of the X-Hasura-Admin-Secret
.
We use shadcn/ui as our component library. It provides a set of re-usable components that you can copy and paste into your apps.
Key features of shadcn/ui in our project:
Our custom implementations and extensions of shadcn/ui components can be found in the libs/ui/components
directory. We've adapted these components to fit our specific needs while maintaining the core benefits of the library.
For more information on how we use and customize shadcn/ui, refer to our UI Components Storybook.
Jest is the test runner used for unit and integration tests. To run all the Jest tests on affected code, you can use the command:
pnpm affected:test
The global settings for Jest are located in tools/test. This directory contains a docker-compose file and an env file to launch specific services used for the integration tests:
test-db
: a database for testing running in memory to speed up execution.hasura-engine
: used to interact with the test-db and services, it uses all the metadata and migrations from the one we used in dev.jest.preset.js
and jest.setup.ts
are referencing all the needed setup to launch the tests. It checks that the hasura-console is running and is healthy.Coverage for all the libraries is created in the root of the workspace. In order to maintain code quality, you can uncomment this section with the minimum coverage before the test reports a failure on CI:Coverage for all the libs is created in the root of the workspace. In order to maintain code quality, you can uncommit this section with the minimum coverage before the test report a failure on CI:
{
// global: {
// branches: 80,
// functions: 80,
// lines: 80,
// statements: 80,
// },
}
Playwright is the test runner used for e2e tests. The tests for the web app are located in apps/web/e2e
and apps/back-office/e2e
.
Before running the tests, be sure that all the service containers are running with:
pnpm docker:services
The test command will wait for all the necessary services to be reachable before launching Playwright.
To run all the Playwright tests on affected code, you can use the command:
pnpm affected:e2e
This project was generated using Nx.
🔎 Smart, Fast and Extensible Build System
In a workspace, libraries are typically divided into four different types:
Libraries that implement "smart" UI (e.g. is effectful, is connected to data sources, handles routing, etc.) for specific business use cases.
Libraries that contain only presentational components. That is, compo- nents that render purely from their props, and calls function handlers when interaction occurs.
Libraries that contain the means for interacting with external data services; external services are typically backend services.
Libraries that contain common utilities that are shared by many projects.
In case you need your own image instead of sebpalluel/hasura_cli_with_socat_and_curl
you can do the following command to publish it in docker hub.
Be sure to have activated the buildx module first by following this article
cd hasura && docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t <username>/<image>:latest --push .
Our platform leverages a variety of cutting-edge services to deliver a robust and feature-rich experience. Here's an overview of our key technology partners:
This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.