├── .eslintignore ├── .eslintrc.js ├── .eslintrc.react.js ├── .gitbook.yaml ├── .gitbook └── assets │ └── listing_transaction_flow-listing_transaction_flow_2.png ├── .github └── workflows │ ├── backend.yml │ ├── extract-translations.yml │ ├── lint.yml │ └── snyk.yml ├── .gitignore ├── .prettierrc.json ├── .snyk ├── CHANGELOG.md ├── LICENSE ├── README.md ├── SECURITY.md ├── TODO ├── TRANSLATION.md ├── backend ├── .gitignore ├── .prettierignore ├── Procfile ├── README.md ├── app.js ├── app.json ├── config.js ├── config │ ├── README.md │ ├── baseConfig.js │ └── templates │ │ ├── affiliate │ │ └── config.json │ │ ├── empty │ │ ├── collections.json │ │ ├── config.json │ │ └── products.json │ │ ├── multi-product │ │ ├── collections.json │ │ ├── config.json │ │ ├── cut-out-the-middleman-baseball-tee │ │ │ ├── 520 │ │ │ │ └── img-0.png │ │ │ ├── data.json │ │ │ └── orig │ │ │ │ └── img-0.png │ │ ├── logo.svg │ │ ├── origin-coffee-mug │ │ │ ├── 520 │ │ │ │ ├── img-0.png │ │ │ │ └── img-1.png │ │ │ ├── data.json │ │ │ └── orig │ │ │ │ ├── img-0.png │ │ │ │ └── img-1.png │ │ ├── origin-logo-tee │ │ │ ├── 520 │ │ │ │ └── img-0.png │ │ │ ├── data.json │ │ │ └── orig │ │ │ │ └── img-0.png │ │ ├── products.json │ │ ├── shipping.json │ │ └── style.css │ │ └── single-product │ │ ├── config.json │ │ ├── logo.svg │ │ ├── origin-coffee-mug │ │ ├── 520 │ │ │ ├── img-0.png │ │ │ └── img-1.png │ │ ├── data.json │ │ └── orig │ │ │ ├── img-0.png │ │ │ └── img-1.png │ │ ├── products.json │ │ ├── shipping.json │ │ └── style.css ├── db │ ├── config.js │ └── migrations │ │ ├── 20200226172508-createTables.js │ │ ├── 20200226214925-addSessions.js │ │ ├── 20200306163357-addShopPassword.js │ │ ├── 20200316190719-addOrderStatusStr.js │ │ ├── 20200317190719-addIpfs.js │ │ ├── 20200330190719-addReferrer.js │ │ ├── 20200408190719-addSellerShop.js │ │ ├── 20200420190719-addOrderDates.js │ │ ├── 20200428190719-addNetworkFields.js │ │ ├── 20200507190719-addExternalPayments.js │ │ ├── 20200519035456-addShopDeployments.js │ │ ├── 20200525115512-AddAcceptedToExternalPayment.js │ │ ├── 20200527204305-alterShopsDataJson.js │ │ ├── 20200527205418-alterExternalOrdersDataJson.js │ │ ├── 20200527205424-alterExternalPaymentsDataJson.js │ │ ├── 20200609202220-addAffiliates.js │ │ ├── 20200609212533-addShopDeploymentName.js │ │ ├── 20200629101157-addVerifyEmailField.js │ │ ├── 20200702115957-addHasChangesCol.js │ │ ├── 20200709112529-addExternalEvents.js │ │ ├── 20200710060151-alterShopDeployment.js │ │ ├── 20200710134411-changeEventTypeToEnum.js │ │ ├── 20200715125957-addPublicSignups.js │ │ ├── 20200715221932-addIpfsGatewayToShopDeployments.js │ │ ├── 20200722203947-addColumnsToTransactions.js │ │ ├── 20200727184749-addStatusToShopDeployments.js │ │ ├── 20200731205137-addPaymentCodeToOrder.js │ │ ├── 20200804043644-addWalletAddressToShop.js │ │ ├── 20200811035456-addShopDomains.js │ │ ├── 20200811234518-addEtlJobs.js │ │ ├── 20200811234546-addEtlShops.js │ │ ├── 20200817211350-addTransationTypePayment.js │ │ ├── 20200818055205-addPaymentSessions.js │ │ ├── 20200824213439-alterOrders.js │ │ ├── 20200827212513-addListingIdToNetworks.js │ │ ├── 20200910055828-addColumnUseMarketplaceToNetwork.js │ │ ├── 20200923070907-update-payment-state.js │ │ ├── 20200924113715-add-payment-type.js │ │ ├── 20201005202813-addAdminLog.js │ │ ├── 20201008224340-add-bucket-urls.js │ │ ├── 20201013034846-addArchivedCol.js │ │ ├── 20201014164908-add-deployment-uuid.js │ │ ├── 20201020191102-add-shop-domain-ip.js │ │ ├── 20201028181012-add-cdn-option.js │ │ ├── 20201029063356-addProducts.js │ │ ├── 20201030222628-add-domain-index.js │ │ ├── 20201103132608-updateDiscountTypeEnum.js │ │ ├── 20201103132751-addDiscountDataCol.js │ │ ├── 20201105121828-addDiscountCols.js │ │ ├── 20201110120605-addExcludeShipping.js │ │ ├── 20201110181336-dbModelCleanUp.js │ │ ├── 20201110195338-add-shop-indexes.js │ │ ├── 20201111223056-addColumnActiveToEtlShop.js │ │ ├── 20201201125225-addPrintfulSyncing.js │ │ ├── 202102141140-addExcludeTaxes.js │ │ └── 20210717101512-addPolicies.js ├── docs │ ├── README.md │ └── api.md ├── etl │ └── processor.js ├── index.js ├── listener.js ├── logic │ ├── deploy │ │ ├── bucket │ │ │ ├── aws.js │ │ │ ├── gcp.js │ │ │ └── index.js │ │ ├── build.js │ │ ├── cdn │ │ │ ├── aws.js │ │ │ ├── gcp.js │ │ │ └── index.js │ │ ├── common.js │ │ ├── dns.js │ │ ├── index.js │ │ ├── ipfs.js │ │ └── records.js │ ├── discount │ │ └── index.js │ ├── order │ │ └── index.js │ ├── payments │ │ ├── paypal.js │ │ └── stripe.js │ ├── printful │ │ ├── api.js │ │ ├── index.js │ │ ├── sync │ │ │ ├── downloadPrintfulMockups.js │ │ │ ├── downloadProductData.js │ │ │ ├── resizePrintfulMockups.js │ │ │ └── writeProductData.js │ │ └── webhook.js │ ├── products │ │ └── index.js │ ├── shop │ │ ├── config.js │ │ ├── create.js │ │ ├── health.js │ │ └── upload.js │ └── themes.js ├── models │ ├── admin_log.js │ ├── affiliate.js │ ├── discount.js │ ├── etl_job.js │ ├── etl_shop.js │ ├── event.js │ ├── external_event.js │ ├── external_payment.js │ ├── index.js │ ├── mig_order.js │ ├── network.js │ ├── order.js │ ├── payment_session.js │ ├── policies.js │ ├── product.js │ ├── seller.js │ ├── seller_shop.js │ ├── shop.js │ ├── shop_deployment.js │ ├── shop_domain.js │ └── transaction.js ├── package.json ├── queues │ ├── autosslProcessor.js │ ├── deploymentProcessor.js │ ├── dnsStatusProcessor.js │ ├── etlProcessor.js │ ├── eventsProcessor.js │ ├── fallbackQueue.js │ ├── index.js │ ├── makeOfferProcessor.js │ ├── printfulSyncProcessor.js │ ├── queues.js │ ├── txProcessor.js │ └── ui.js ├── routes │ ├── _auth.js │ ├── _makeOffer.js │ ├── affiliate.js │ ├── auth.js │ ├── collections.js │ ├── crypto.js │ ├── discounts.js │ ├── domains.js │ ├── exchange-rates.js │ ├── health.js │ ├── networks.js │ ├── offline-payment.js │ ├── orders.js │ ├── paypal.js │ ├── policies.js │ ├── printful.js │ ├── products.js │ ├── shipping-zones.js │ ├── shops.js │ ├── stripe.js │ ├── themes.js │ ├── tx.js │ ├── uphold.js │ └── users.js ├── scripts │ ├── autoVariant.js │ ├── aws │ │ └── marketplace │ │ │ ├── README.md │ │ │ ├── build.sh │ │ │ └── startup_script.sh │ ├── backfill.js │ ├── configCli.js │ ├── createListing.js │ ├── csv.js │ ├── gcp │ │ └── marketplace │ │ │ ├── GCP_Deployment_Guide.pdf │ │ │ ├── README.md │ │ │ ├── build.sh │ │ │ └── startup_script.sh │ ├── import-origin.js │ ├── import-printful.js │ ├── import-shopify.js │ ├── one-off │ │ ├── backfillPaymentTypes.js │ │ ├── backfillShopsWallet.js │ │ ├── cryptoDirect.js │ │ ├── fix-printful-variant-ids.js │ │ └── updateShopsConfigJson.js │ ├── printful.js │ ├── printful │ │ ├── downloadMockups.js │ │ ├── downloadMockups2.js │ │ ├── generateMockups.js │ │ ├── getMockups.js │ │ ├── getOrders.js │ │ ├── getProduct.js │ │ ├── getProductIds.js │ │ ├── getProducts.js │ │ ├── getSizeGuide.js │ │ └── writeInternalData.js │ ├── printful2.js │ ├── reprocessEvent.js │ ├── runEtl.js │ ├── stripe.js │ ├── updateListing.js │ ├── uphold.js │ ├── validate.js │ └── validatePayments.js ├── sentry.js ├── test │ ├── README.md │ ├── cart.test.js │ ├── config.test.js │ ├── const.js │ ├── crypto.test.js │ ├── discount.test.js │ ├── dns.utils.test.js │ ├── email.test.js │ ├── http.utils.test.js │ ├── inventory.test.js │ ├── ipfs.utils.test.js │ ├── listing.test.js │ ├── offer.test.js │ ├── order.test.js │ ├── policies.test.js │ ├── printful.test.js │ ├── setup.js │ ├── shop.test.js │ ├── shop.util.test.js │ └── utils.js └── utils │ ├── _abi.js │ ├── _abi_proxy.js │ ├── _ipfs.js │ ├── address.js │ ├── array.js │ ├── autossl.js │ ├── cartData.js │ ├── collections.js │ ├── const.js │ ├── createListing.js │ ├── discordWebhook.js │ ├── dns │ ├── clouddns.js │ ├── cloudflare.js │ ├── index.js │ └── route53.js │ ├── emails │ ├── _getTransport.js │ ├── newOrder.js │ ├── passwordReset.js │ ├── printfulOrderFailed.js │ ├── sellerContact.js │ ├── stripeWebhookError.js │ ├── templates │ │ ├── _head.js │ │ ├── _orderItem.js │ │ ├── _orderItemTxt.js │ │ ├── forgotPassEmail.js │ │ ├── forgotPassEmailTxt.js │ │ ├── newOrder.js │ │ ├── newOrderTxt.js │ │ ├── newOrderVendor.js │ │ ├── printfulOrderFailed.js │ │ ├── printfulOrderFailedTxt.js │ │ ├── sellerContact.js │ │ ├── sellerContactTxt.js │ │ ├── stripeWebhookError.js │ │ ├── stripeWebhookErrorTxt.js │ │ ├── verifyEmail.js │ │ └── verifyEmailTxt.js │ └── verifyEmail.js │ ├── encryptedConfig.js │ ├── enums.js │ ├── events.js │ ├── filesystem.js │ ├── google.js │ ├── handleLog.js │ ├── hostCache.js │ ├── http.js │ ├── id.js │ ├── ip.js │ ├── logger.js │ ├── module.js │ ├── multiaddr.js │ ├── offer.js │ ├── orders.js │ ├── paypal.js │ ├── pgp.js │ ├── primeIpfs.js │ ├── sellers.js │ ├── shop.js │ ├── string.js │ ├── stripe.js │ └── validators.js ├── cloudbuild.yaml ├── crowdin.yml ├── devops ├── Dockerfile ├── build.sh ├── cloudbuild.dshop0.yaml ├── cloudbuild.ui.yaml ├── dockerfiles │ ├── cert-issuer-ingress │ └── files │ │ ├── issuer-entrypoint.sh │ │ └── issuer-nginx.conf.template ├── helm │ ├── README.md │ ├── charts │ │ └── dshop │ │ │ ├── .helmignore │ │ │ ├── Chart.yaml │ │ │ ├── templates │ │ │ ├── NOTES.txt │ │ │ ├── _helpers.tpl │ │ │ ├── dshop.balancer.yaml │ │ │ ├── dshop.deployment.yaml │ │ │ ├── dshop.hpa.yaml │ │ │ ├── dshop.ingress.yaml │ │ │ ├── dshop.service.yaml │ │ │ ├── dshop.serviceaccount.yaml │ │ │ ├── dshopcache.pvc.yaml │ │ │ ├── env.secrets.yaml │ │ │ ├── issuer.sts.yaml │ │ │ ├── issuer.svc.yaml │ │ │ ├── nfs.pv.yaml │ │ │ ├── redis.configmap.yaml │ │ │ ├── redis.sts.yaml │ │ │ ├── redis.svc.yaml │ │ │ └── tests │ │ │ │ └── test-connection.yaml │ │ │ └── values.yaml │ ├── helmfile.yaml │ └── values │ │ ├── dshop │ │ ├── README.md │ │ ├── default.yaml │ │ ├── mainnet.yaml │ │ ├── rinkeby.yaml │ │ ├── secrets.mainnet.yaml │ │ └── secrets.rinkeby.yaml │ │ └── monitoring │ │ ├── secrets.yaml │ │ └── values.yaml ├── syncBucket.js └── updateBuilds.js ├── docs ├── .gitbook │ └── assets │ │ ├── dshop-flow.png │ │ ├── dshop-offer.png │ │ ├── listing_transaction_flow-listing_transaction_flow_2.png │ │ └── origin-header.png ├── README.md ├── SUMMARY.md ├── dshop │ ├── backend.md │ ├── data-import.md │ ├── deployment │ │ ├── aws.md │ │ ├── first-run.md │ │ ├── gcp.md │ │ └── images │ │ │ ├── aws-accept-terms.png │ │ │ ├── aws-continue-to-config.png │ │ │ ├── aws-continue-to-launch.png │ │ │ ├── aws-launch-1.png │ │ │ ├── aws-launch-2.png │ │ │ ├── aws-launch-3.png │ │ │ ├── aws-route53-1.png │ │ │ ├── aws-route53-2.png │ │ │ ├── aws-route53-3.png │ │ │ ├── firstrun-configuration.png │ │ │ ├── firstrun-registration.png │ │ │ ├── gcp-deployment-finished.png │ │ │ ├── gcp-deployment.png │ │ │ ├── gcp-dns-records.png │ │ │ ├── gcp-instance-config.png │ │ │ ├── gcp-instance-details.png │ │ │ └── gcp-marketplace.png │ ├── dshop.md │ ├── front-end.md │ └── order-fulfillment.md ├── smart-contract │ ├── api.md │ ├── commissions.md │ ├── disputes.md │ ├── marketplace-smart-contract.md │ └── transaction-flow.md └── themes │ ├── config-fields.md │ ├── create-theme.md │ └── overview.md ├── lerna.json ├── origin-header.png ├── package.json ├── packages ├── contracts-new │ ├── .gitattributes │ ├── .gitignore │ ├── buidler.config.js │ ├── contracts │ │ ├── ERC20FixedSupplyBurnable.sol │ │ └── WETH.sol │ ├── package.json │ ├── scripts │ │ └── sample-script.js │ └── test │ │ └── sample-test.js ├── contracts │ ├── .gitattributes │ ├── .gitignore │ ├── .solcover.js │ ├── .soliumignore │ ├── .soliumrc.json │ ├── README.md │ ├── build │ │ ├── contracts_mainnet.json │ │ ├── contracts_origin.json │ │ └── contracts_rinkeby.json │ ├── contracts │ │ ├── Migrations.sol │ │ ├── marketplace │ │ │ └── V01_Marketplace.sol │ │ └── token │ │ │ ├── MockOUSD.sol │ │ │ ├── OriginToken.sol │ │ │ ├── README.md │ │ │ ├── TestToken.sol │ │ │ ├── TokenMigration.sol │ │ │ └── WhitelistedPausableToken.sol │ ├── migrations │ │ ├── 1_initial_migration.js │ │ ├── 2_deploy_marketplace_contracts.js │ │ ├── 3_transfer_tokens_for_dev.js │ │ └── README.md │ ├── package.json │ └── truffle.js ├── dshop-validation │ ├── index.js │ ├── matrix.js │ ├── package.json │ ├── resourceMatrix.json │ └── test │ │ ├── fixtures │ │ └── matrix1.json │ │ └── matrix.js ├── ipfs │ ├── .eslintrc.js │ ├── README.md │ ├── package.json │ └── src │ │ └── index.js ├── services │ ├── .eslintrc.js │ ├── .gitignore │ ├── README.md │ ├── data │ │ ├── .gitignore │ │ ├── localhost.cert │ │ └── localhost.key │ ├── fixtures │ │ ├── gift-cards │ │ │ ├── amazon.png │ │ │ ├── default.jpg │ │ │ ├── itunes.png │ │ │ ├── starbucks.png │ │ │ ├── target.png │ │ │ └── walmart.png │ │ ├── hawaii-house │ │ │ ├── hawaii-house.json │ │ │ ├── image-1.jpg │ │ │ ├── image-2.jpg │ │ │ ├── image-3.jpg │ │ │ ├── image-4.jpg │ │ │ └── image-5.jpg │ │ ├── lake-house │ │ │ ├── image-1.jpg │ │ │ ├── image-2.jpg │ │ │ ├── image-3.jpg │ │ │ ├── image-4.jpg │ │ │ ├── image-5.jpg │ │ │ ├── image-6.jpg │ │ │ └── lake-house.json │ │ ├── listing-valid.json │ │ ├── offer-valid.json │ │ ├── origin-spaceman │ │ │ ├── image-1.jpg │ │ │ └── origin-spaceman.json │ │ ├── profile-valid.json │ │ ├── review-valid.json │ │ ├── scout │ │ │ ├── image-1.jpg │ │ │ ├── image-2.jpg │ │ │ ├── image-3.jpg │ │ │ ├── image-4.jpg │ │ │ └── scout.json │ │ ├── taylor-swift-tix │ │ │ ├── image-1.jpg │ │ │ ├── image-2.jpg │ │ │ ├── image-3.jpg │ │ │ ├── image-4.jpg │ │ │ ├── image-5.jpg │ │ │ └── taylor-swift-tix.json │ │ ├── updated-listing.json │ │ └── zinc-house │ │ │ ├── image-1 │ │ │ ├── image-1.jpg │ │ │ ├── image-10.jpg │ │ │ ├── image-2.jpg │ │ │ ├── image-3.jpg │ │ │ ├── image-4.jpg │ │ │ ├── image-5.jpg │ │ │ ├── image-7.jpg │ │ │ ├── image-8.jpg │ │ │ ├── image-9.jpg │ │ │ └── zinc-house.json │ ├── index.js │ ├── package.json │ └── start.js └── utils │ ├── .eslintrc.js │ ├── .gitignore │ ├── Countries.js │ ├── ProcessingTimes.js │ ├── README.md │ ├── calculateCartTotal.js │ ├── dns.js │ ├── formatPrice.js │ ├── generatePrintfulOrder.js │ ├── marketplace.js │ ├── memoize.js │ ├── package.json │ └── stripe.js ├── scripts ├── clean-package-locks.sh ├── package-versions.js ├── pinata_pin_hashes.js ├── test.js └── wait-for.sh ├── shop ├── .eslintrc.js ├── .gitignore ├── .prettierignore ├── CHANGELOG.md ├── README.md ├── buildThemeDev.sh ├── buildThemes.sh ├── dev.env ├── docs │ ├── assets │ │ ├── dshop-flow.afdesign │ │ ├── dshop-flow.png │ │ ├── dshop-offer.afdesign │ │ └── dshop-offer.png │ ├── index.md │ └── order.md ├── index.js ├── package.json ├── public │ ├── images │ │ ├── add-icon.svg │ │ ├── affiliate-bnr-sm.svg │ │ ├── affiliate-header.svg │ │ ├── affiliate-header2.svg │ │ ├── affiliate-link-icon.svg │ │ ├── affiliate-logo.svg │ │ ├── affiliate-sign.svg │ │ ├── amex.svg │ │ ├── aws-ses.png │ │ ├── background-graphic.svg │ │ ├── box.svg │ │ ├── caret-dark.svg │ │ ├── cart-graphic.svg │ │ ├── close-button-lg.svg │ │ ├── coins.svg │ │ ├── connect_with_uphold.svg │ │ ├── default-image.svg │ │ ├── delete-icon.svg │ │ ├── desktop-icon.svg │ │ ├── discover.svg │ │ ├── dshop-logo-blue.svg │ │ ├── dshop-logo.svg │ │ ├── edit-icon.svg │ │ ├── empty-stores.svg │ │ ├── error-icon.svg │ │ ├── fire-icon.svg │ │ ├── green-checkmark-circle.svg │ │ ├── green-checkmark.svg │ │ ├── info-icon.svg │ │ ├── left-arrow-large.svg │ │ ├── mailgun.png │ │ ├── master.svg │ │ ├── metamask.svg │ │ ├── mobile-icon.svg │ │ ├── new-shop │ │ │ ├── box.svg │ │ │ ├── globe.svg │ │ │ ├── link.svg │ │ │ ├── photo.svg │ │ │ └── text.svg │ │ ├── new-window-icon.svg │ │ ├── ogn-icon.svg │ │ ├── onboarding-banner-bg.svg │ │ ├── origin-affiliates.svg │ │ ├── payment │ │ │ ├── dai.svg │ │ │ ├── eth.svg │ │ │ └── ogn.svg │ │ ├── powered_by_stripe.svg │ │ ├── powered_by_uphold.svg │ │ ├── printful.svg │ │ ├── right-arrow-large.svg │ │ ├── screenshot-discount.png │ │ ├── screenshot-discount@2x.png │ │ ├── screenshot-discount@3x.png │ │ ├── screenshot-note.png │ │ ├── screenshot-note@2x.png │ │ ├── screenshot-note@3x.png │ │ ├── sendgrid.png │ │ ├── spinner-animation-dark.svg │ │ ├── spinner-blue.svg │ │ ├── spinner.svg │ │ ├── themes-icon.svg │ │ ├── upload-icon-gray.svg │ │ ├── upload.svg │ │ ├── visa.svg │ │ └── wallet-icon.svg │ ├── template-cdn.html │ └── template.html ├── scripts │ ├── crowdinToFbt.js │ ├── fbtToCrowdin.js │ ├── genKey.js │ ├── getCss.js │ └── splitTranslations.js ├── src │ ├── components │ │ ├── ConfigLoader.js │ │ ├── ConfirmationModal.js │ │ ├── CountrySelect.js │ │ ├── CurrencySelect.js │ │ ├── DisplayShopPolicy.js │ │ ├── DshopProvider.js │ │ ├── Gallery.css │ │ ├── Gallery.js │ │ ├── GalleryOld.js │ │ ├── GalleryScroll.js │ │ ├── ImageCropperModal.js │ │ ├── ImagePicker.js │ │ ├── Link.js │ │ ├── Loading.js │ │ ├── LoadingButton.js │ │ ├── LocaleSelect.js │ │ ├── Modal.js │ │ ├── NoItems.js │ │ ├── OfflinePaymentInstructions.js │ │ ├── Paginate.js │ │ ├── PaymentStatusText.js │ │ ├── Popover.js │ │ ├── PreviewBanner.js │ │ ├── Price.js │ │ ├── ProductImage.js │ │ ├── ProductList.js │ │ ├── ProductRedirect.js │ │ ├── ProgressBar.js │ │ ├── ProvinceSelect.js │ │ ├── Redirect.js │ │ ├── ReportErrorModal.js │ │ ├── SentryErrorBoundary.js │ │ ├── ShippingForm.js │ │ ├── SimilarProducts.js │ │ ├── SocialLink.js │ │ ├── SocialLinks.js │ │ ├── SortBy.js │ │ ├── SortableTable.js │ │ ├── SwitchToStorefront.js │ │ ├── Toaster.js │ │ ├── Toggle.js │ │ ├── Tooltip.js │ │ ├── VariantOptions.js │ │ ├── VariantPic.js │ │ ├── Web3Transaction.js │ │ ├── Web3WalletConnect.js │ │ ├── admin │ │ │ ├── Menu.js │ │ │ ├── PasswordField.js │ │ │ ├── ShopSearch.js │ │ │ └── Styles.js │ │ ├── custom │ │ │ ├── AffiliateBanner.js │ │ │ ├── CreateMug.js │ │ │ └── ModalMug.js │ │ ├── icons │ │ │ ├── Admin.js │ │ │ ├── Bars.js │ │ │ ├── Camera.js │ │ │ ├── Caret.js │ │ │ ├── Cart.js │ │ │ ├── CheckCircle.js │ │ │ ├── Clipboard.js │ │ │ ├── Close.js │ │ │ ├── Discord.js │ │ │ ├── Ethereum.js │ │ │ ├── Facebook.js │ │ │ ├── Instagram.js │ │ │ ├── Link.js │ │ │ ├── Medium.js │ │ │ ├── Menu.js │ │ │ ├── OGN.js │ │ │ ├── Plus.js │ │ │ ├── Reddit.js │ │ │ ├── Search.js │ │ │ ├── Spinner.js │ │ │ ├── Telegram.js │ │ │ ├── Twitter.js │ │ │ └── YouTube.js │ │ ├── payment │ │ │ ├── Chooser.js │ │ │ ├── NoPaymentDue.js │ │ │ ├── OfflinePayment.js │ │ │ ├── PayPal.js │ │ │ ├── Stripe.js │ │ │ ├── crypto │ │ │ │ ├── Crypto.js │ │ │ │ ├── CryptoDirect.js │ │ │ │ ├── _TokenList.js │ │ │ │ └── _WalletNotReady.js │ │ │ └── uphold │ │ │ │ ├── Uphold.js │ │ │ │ ├── _Connect.js │ │ │ │ ├── _CurrencyStyles.js │ │ │ │ └── _Logout.js │ │ └── settings │ │ │ └── ProcessorsList.js │ ├── css │ │ ├── app.css │ │ └── app.scss │ ├── data │ │ ├── Countries.js │ │ ├── Currencies.js │ │ ├── Languages.js │ │ ├── Networks.js │ │ ├── OfferStates.js │ │ ├── PaymentStates.js │ │ ├── PaymentTypes.js │ │ ├── addData.js │ │ ├── defaultTokens.js │ │ ├── fbTrack.js │ │ ├── fetchConfig.js │ │ ├── fetchProduct.js │ │ ├── fetchProductStock.js │ │ ├── formData.js │ │ ├── state.js │ │ └── validations │ │ │ └── checkoutInfo.js │ ├── index.js │ ├── pages │ │ ├── About.js │ │ ├── App.js │ │ ├── Main.js │ │ ├── Order.js │ │ ├── OrderLoader.js │ │ ├── Password.js │ │ ├── Privacy.js │ │ ├── Product.js │ │ ├── Products.js │ │ ├── Storefront.js │ │ ├── Terms.js │ │ ├── _Categories.js │ │ ├── _Footer.js │ │ ├── _MobileMenu.js │ │ ├── _Nav.js │ │ ├── _Notice.js │ │ ├── _Search.js │ │ ├── admin │ │ │ ├── Admin.js │ │ │ ├── Loader.js │ │ │ ├── Orders.js │ │ │ ├── StoreSelector.js │ │ │ ├── _AccountSelector.js │ │ │ ├── _LiveChat.js │ │ │ ├── _Menu.js │ │ │ ├── _Nav.js │ │ │ ├── _NewShop.js │ │ │ ├── _PublishChanges.js │ │ │ ├── _User.js │ │ │ ├── auth │ │ │ │ ├── Auth.js │ │ │ │ ├── ForgotPass.js │ │ │ │ ├── Login.js │ │ │ │ ├── ResetPass.js │ │ │ │ └── SignUp.js │ │ │ ├── collections │ │ │ │ ├── List.js │ │ │ │ ├── Show.js │ │ │ │ ├── _AddProducts.js │ │ │ │ ├── _Delete.js │ │ │ │ └── _New.js │ │ │ ├── dashboard │ │ │ │ ├── Dashboard.js │ │ │ │ └── _Chart.js │ │ │ ├── discounts │ │ │ │ ├── Edit.js │ │ │ │ ├── List.js │ │ │ │ ├── PaymentSpecific.js │ │ │ │ ├── _Delete.js │ │ │ │ └── _Tabs.js │ │ │ ├── onboarding │ │ │ │ ├── Onboarding.js │ │ │ │ ├── _ArticleItem.js │ │ │ │ ├── _Banner.js │ │ │ │ └── _TaskItem.js │ │ │ ├── order │ │ │ │ ├── Contract.js │ │ │ │ ├── Details.js │ │ │ │ ├── Events.js │ │ │ │ ├── Order.js │ │ │ │ ├── Printful.js │ │ │ │ ├── _CustomerInfo.js │ │ │ │ ├── _OffchainPaymentActions.js │ │ │ │ ├── _PaymentActions.js │ │ │ │ └── _PaymentInfo.js │ │ │ ├── products │ │ │ │ ├── Edit.js │ │ │ │ ├── List.js │ │ │ │ ├── README.md │ │ │ │ ├── _Delete.js │ │ │ │ ├── _EditOption.js │ │ │ │ ├── _EditVariants.js │ │ │ │ ├── _LinkCollections.js │ │ │ │ ├── _SelectVariantImage.js │ │ │ │ └── _TokenComponent.js │ │ │ ├── settings │ │ │ │ ├── Advanced.js │ │ │ │ ├── Console.js │ │ │ │ ├── List.js │ │ │ │ ├── Server.js │ │ │ │ ├── Settings.js │ │ │ │ ├── _VerifyButton.js │ │ │ │ ├── appearance │ │ │ │ │ ├── Edit.js │ │ │ │ │ ├── _UploadFile.js │ │ │ │ │ └── social-links │ │ │ │ │ │ ├── SocialLinks.js │ │ │ │ │ │ ├── _Delete.js │ │ │ │ │ │ ├── _Edit.js │ │ │ │ │ │ └── _networks.js │ │ │ │ ├── apps │ │ │ │ │ ├── AWSModal.js │ │ │ │ │ ├── List.js │ │ │ │ │ ├── MailgunModal.js │ │ │ │ │ ├── PrintfulModal.js │ │ │ │ │ ├── PrintfulSync.js │ │ │ │ │ └── SendgridModal.js │ │ │ │ ├── checkout │ │ │ │ │ ├── Edit.js │ │ │ │ │ ├── _CountryTaxEntry.js │ │ │ │ │ ├── _Taxes.js │ │ │ │ │ └── _taxInputValidation.js │ │ │ │ ├── deployments │ │ │ │ │ ├── List.js │ │ │ │ │ └── _DeployButton.js │ │ │ │ ├── general │ │ │ │ │ ├── Edit.js │ │ │ │ │ └── domains │ │ │ │ │ │ ├── List.js │ │ │ │ │ │ ├── _AddButton.js │ │ │ │ │ │ ├── _DeleteModal.js │ │ │ │ │ │ ├── _DomainStatus.js │ │ │ │ │ │ ├── _EditHostname.js │ │ │ │ │ │ ├── _EditModal.js │ │ │ │ │ │ ├── _Input.js │ │ │ │ │ │ ├── _Instructions.js │ │ │ │ │ │ ├── _OgnSubdomainStatus.js │ │ │ │ │ │ └── _Publish.js │ │ │ │ ├── legal │ │ │ │ │ └── Edit.js │ │ │ │ ├── payments │ │ │ │ │ ├── CryptoSettings.js │ │ │ │ │ ├── List.js │ │ │ │ │ ├── OfflinePayments.js │ │ │ │ │ ├── PayPalModal.js │ │ │ │ │ ├── StripeModal.js │ │ │ │ │ ├── UpholdModal.js │ │ │ │ │ ├── Web3Modal.js │ │ │ │ │ ├── _ConnectModal.js │ │ │ │ │ ├── _ConnectWallet.js │ │ │ │ │ ├── _CreateListing.js │ │ │ │ │ ├── _CreateListingTx.js │ │ │ │ │ ├── _CustomTokenModal.js │ │ │ │ │ ├── _DeleteOfflinePayment.js │ │ │ │ │ ├── _DisconnectModal.js │ │ │ │ │ └── _OfflinePaymentModal.js │ │ │ │ ├── shipping │ │ │ │ │ ├── Edit.js │ │ │ │ │ ├── _CountriesMultiSelect.js │ │ │ │ │ ├── _PrintfulShippingAlert.js │ │ │ │ │ ├── _RateEdit.js │ │ │ │ │ └── _ShippingDestination.js │ │ │ │ └── users │ │ │ │ │ ├── List.js │ │ │ │ │ └── _Add.js │ │ │ └── themes │ │ │ │ ├── Customize.js │ │ │ │ ├── List.js │ │ │ │ ├── _EditFields.js │ │ │ │ ├── _Section.js │ │ │ │ └── fields │ │ │ │ ├── CollectionsList.js │ │ │ │ ├── ColorPalettes.js │ │ │ │ ├── Media.js │ │ │ │ ├── ProductsList.js │ │ │ │ ├── Range.js │ │ │ │ ├── Select.js │ │ │ │ ├── Text.js │ │ │ │ └── _ImagePicker.js │ │ ├── affiliates │ │ │ ├── Affiliates.js │ │ │ ├── JoinModal.js │ │ │ ├── Landing.js │ │ │ └── NavBar.js │ │ ├── cart │ │ │ ├── Cart.js │ │ │ └── CartItem.js │ │ ├── checkout │ │ │ ├── Checkout.js │ │ │ ├── CheckoutItem.js │ │ │ ├── ChoosePayment.js │ │ │ ├── Discount.js │ │ │ ├── Donation.js │ │ │ ├── Information.js │ │ │ ├── Loader.js │ │ │ ├── Payment.js │ │ │ ├── Shipping.js │ │ │ ├── Summary.js │ │ │ ├── _BetaWarning.js │ │ │ ├── _BillingAddress.js │ │ │ ├── _Contact.js │ │ │ └── _ShipTo.js │ │ ├── product │ │ │ └── SizeGuide.js │ │ └── super-admin │ │ │ ├── Dashboard.js │ │ │ ├── Loader.js │ │ │ ├── SuperAdmin.js │ │ │ ├── _Menu.js │ │ │ ├── networks │ │ │ ├── Edit.js │ │ │ ├── List.js │ │ │ ├── New.js │ │ │ ├── _Form.js │ │ │ └── _MakeActiveButton.js │ │ │ ├── setup │ │ │ ├── FirstTime.js │ │ │ ├── ServerSetup.js │ │ │ ├── SignUp.js │ │ │ ├── _Button.js │ │ │ ├── _ErrorText.js │ │ │ ├── _SetupLayout.js │ │ │ └── _formStyles.js │ │ │ ├── shops │ │ │ ├── List.js │ │ │ ├── NewShop.js │ │ │ ├── Show.js │ │ │ ├── new-shop │ │ │ │ ├── CreateListing.js │ │ │ │ ├── CreateShop.js │ │ │ │ ├── ShopReady.js │ │ │ │ └── _FetchShopConfig.js │ │ │ └── shop │ │ │ │ ├── Assets.js │ │ │ │ ├── Deploy.js │ │ │ │ ├── FileEditor.js │ │ │ │ ├── Settings.js │ │ │ │ ├── Sync.js │ │ │ │ ├── _ActivateBuild.js │ │ │ │ ├── _Delete.js │ │ │ │ ├── _DeleteAsset.js │ │ │ │ ├── _DeployButton.js │ │ │ │ ├── _SyncToCacheButton.js │ │ │ │ └── _UploadAssets.js │ │ │ └── users │ │ │ ├── Edit.js │ │ │ ├── List.js │ │ │ ├── New.js │ │ │ ├── Show.js │ │ │ ├── _Delete.js │ │ │ └── _Form.js │ ├── themes │ │ ├── README.md │ │ ├── art │ │ │ ├── app.css │ │ │ ├── components │ │ │ │ ├── About.js │ │ │ │ ├── Contact.js │ │ │ │ ├── Home.js │ │ │ │ ├── Product.js │ │ │ │ ├── Products.js │ │ │ │ ├── Storefront.js │ │ │ │ ├── _Footer.js │ │ │ │ ├── _Header.js │ │ │ │ └── _Products.js │ │ │ ├── index.js │ │ │ ├── screenshot.png │ │ │ ├── tailwind.config.js │ │ │ └── theme.json │ │ ├── cake │ │ │ ├── README.md │ │ │ ├── app.css │ │ │ ├── components │ │ │ │ ├── About.js │ │ │ │ ├── Cart.js │ │ │ │ ├── Home.js │ │ │ │ ├── Product.js │ │ │ │ ├── Products.js │ │ │ │ ├── Storefront.js │ │ │ │ ├── _Footer.js │ │ │ │ ├── _Header.js │ │ │ │ └── _Products.js │ │ │ ├── fonts │ │ │ │ ├── Avenir-Black.ttf │ │ │ │ ├── Avenir-Heavy.ttf │ │ │ │ ├── Avenir-Light.ttf │ │ │ │ ├── Avenir-Medium.ttf │ │ │ │ ├── Philosopher-Bold.ttf │ │ │ │ ├── Philosopher-BoldItalic.ttf │ │ │ │ ├── Philosopher-Italic.ttf │ │ │ │ ├── Philosopher-Regular.ttf │ │ │ │ ├── RobotoSlab-Black.ttf │ │ │ │ ├── RobotoSlab-Bold.ttf │ │ │ │ ├── RobotoSlab-ExtraBold.ttf │ │ │ │ ├── RobotoSlab-ExtraLight.ttf │ │ │ │ ├── RobotoSlab-Light.ttf │ │ │ │ ├── RobotoSlab-Medium.ttf │ │ │ │ ├── RobotoSlab-Regular.ttf │ │ │ │ ├── RobotoSlab-SemiBold.ttf │ │ │ │ ├── RobotoSlab-Thin.ttf │ │ │ │ └── RobotoSlab-VariableFont_wght.ttf │ │ │ ├── index.js │ │ │ ├── screenshot.png │ │ │ ├── tailwind.config.js │ │ │ └── theme.json │ │ ├── poly │ │ │ ├── app.css │ │ │ ├── components │ │ │ │ ├── About.js │ │ │ │ ├── Cart.js │ │ │ │ ├── Home.js │ │ │ │ ├── Product.js │ │ │ │ ├── Products.js │ │ │ │ ├── Storefront.js │ │ │ │ ├── _Footer.js │ │ │ │ ├── _Header.js │ │ │ │ └── _Products.js │ │ │ ├── fonts │ │ │ │ ├── AGaramondPro-Bold.otf │ │ │ │ ├── AGaramondPro-BoldItalic.otf │ │ │ │ ├── AGaramondPro-Italic.otf │ │ │ │ ├── AGaramondPro-Regular.otf │ │ │ │ ├── AGaramondPro-Semibold.otf │ │ │ │ └── AGaramondPro-SemiboldItalic.otf │ │ │ ├── index.js │ │ │ ├── screenshot.png │ │ │ ├── tailwind.config.js │ │ │ └── theme.json │ │ ├── rare │ │ │ ├── Addresses.js │ │ │ ├── TODO.md │ │ │ ├── abi.json │ │ │ ├── app.css │ │ │ ├── components │ │ │ │ ├── BackLink.js │ │ │ │ ├── Button.js │ │ │ │ ├── ButtonPrimary.js │ │ │ │ ├── Chart.js │ │ │ │ ├── Etherscan.js │ │ │ │ ├── LoadingSpinnerC.js │ │ │ │ ├── OrderSummary.js │ │ │ │ ├── Overlay.js │ │ │ │ ├── RedeemSummary.js │ │ │ │ ├── Select.js │ │ │ │ ├── SelectQuantity.js │ │ │ │ ├── SelectToken.js │ │ │ │ └── confetti.js │ │ │ ├── fonts │ │ │ │ ├── IBMPlexSans-Bold.ttf │ │ │ │ ├── IBMPlexSans-BoldItalic.ttf │ │ │ │ ├── IBMPlexSans-ExtraLight.ttf │ │ │ │ ├── IBMPlexSans-ExtraLightItalic.ttf │ │ │ │ ├── IBMPlexSans-Italic.ttf │ │ │ │ ├── IBMPlexSans-Light.ttf │ │ │ │ ├── IBMPlexSans-LightItalic.ttf │ │ │ │ ├── IBMPlexSans-Medium.ttf │ │ │ │ ├── IBMPlexSans-MediumItalic.ttf │ │ │ │ ├── IBMPlexSans-Regular.ttf │ │ │ │ ├── IBMPlexSans-SemiBold.ttf │ │ │ │ ├── IBMPlexSans-SemiBoldItalic.ttf │ │ │ │ ├── IBMPlexSans-Thin.ttf │ │ │ │ └── IBMPlexSans-ThinItalic.ttf │ │ │ ├── index.js │ │ │ ├── pages │ │ │ │ ├── About.js │ │ │ │ ├── Buy.js │ │ │ │ ├── Checkout.js │ │ │ │ ├── Confirmation.js │ │ │ │ ├── Connection.js │ │ │ │ ├── Landing.js │ │ │ │ ├── Product.js │ │ │ │ ├── Redeem.js │ │ │ │ ├── Sell.js │ │ │ │ ├── Stats.js │ │ │ │ ├── Storefront.js │ │ │ │ ├── _Footer.js │ │ │ │ ├── _Header.js │ │ │ │ └── _Products.js │ │ │ ├── screenshot.png │ │ │ ├── tailwind.config.js │ │ │ ├── theme.json │ │ │ └── utils.js │ │ ├── shared │ │ │ ├── Cart.js │ │ │ ├── Confirmation.js │ │ │ ├── Gallery.js │ │ │ ├── ProductOptions.js │ │ │ ├── checkout │ │ │ │ ├── Checkout.js │ │ │ │ ├── Information.js │ │ │ │ ├── Loader.js │ │ │ │ ├── Payment.js │ │ │ │ ├── Shipping.js │ │ │ │ ├── _Discount.js │ │ │ │ ├── _Summary.js │ │ │ │ ├── _Taxes.js │ │ │ │ └── checkout.css │ │ │ └── hoc │ │ │ │ └── usePalette.js │ │ ├── uni │ │ │ ├── Addresses.js │ │ │ ├── TODO.md │ │ │ ├── abi.json │ │ │ ├── app.css │ │ │ ├── components │ │ │ │ ├── BackLink.js │ │ │ │ ├── Button.js │ │ │ │ ├── ButtonPrimary.js │ │ │ │ ├── LoadingSpinner.js │ │ │ │ ├── LoadingSpinnerC.js │ │ │ │ ├── Overlay.js │ │ │ │ ├── Select.js │ │ │ │ ├── SelectQuantity.js │ │ │ │ ├── SelectToken.js │ │ │ │ ├── Title.js │ │ │ │ └── confetti.js │ │ │ ├── fonts │ │ │ │ ├── PTSans-Bold.ttf │ │ │ │ └── PTSans-Regular.ttf │ │ │ ├── index.js │ │ │ ├── pages │ │ │ │ ├── About.js │ │ │ │ ├── Buy.js │ │ │ │ ├── Checkout.js │ │ │ │ ├── Confirmation.js │ │ │ │ ├── Connection.js │ │ │ │ ├── Home.js │ │ │ │ ├── Redeem.js │ │ │ │ ├── Sell.js │ │ │ │ ├── Stats.js │ │ │ │ ├── Storefront.js │ │ │ │ ├── _Footer.js │ │ │ │ ├── _Header.js │ │ │ │ └── _Products.js │ │ │ ├── screenshot.png │ │ │ ├── tailwind.config.js │ │ │ ├── theme.json │ │ │ └── utils.js │ │ └── ybm │ │ │ ├── app.css │ │ │ ├── components │ │ │ ├── About.js │ │ │ ├── Cart.js │ │ │ ├── Contact.js │ │ │ ├── Home.js │ │ │ ├── Product.js │ │ │ ├── Products.js │ │ │ ├── _Collections.js │ │ │ ├── _Footer.js │ │ │ ├── _Header.js │ │ │ └── _Products.js │ │ │ ├── fonts │ │ │ ├── Avenir-Black.ttf │ │ │ ├── Avenir-Heavy.ttf │ │ │ ├── Avenir-Light.ttf │ │ │ └── Avenir-Medium.ttf │ │ │ ├── index.js │ │ │ ├── screenshot.png │ │ │ ├── tailwind.config.js │ │ │ └── theme.json │ └── utils │ │ ├── abis │ │ └── IdentityProxy.js │ │ ├── auth.js │ │ ├── formHelpers.js │ │ ├── formatAddress.js │ │ ├── formatPrice.js │ │ ├── generateVariants.js │ │ ├── getMaxQuantity.js │ │ ├── inventoryUtils.js │ │ ├── listing.js │ │ ├── loadImage.js │ │ ├── maskSecret.js │ │ ├── numberFormat.js │ │ ├── offer.js │ │ ├── parsePlainEmail.js │ │ ├── setLocale.js │ │ ├── shippingTimes.js │ │ ├── sortProducts.js │ │ ├── unstoppable.js │ │ ├── useAbout.js │ │ ├── useActiveTheme.js │ │ ├── useAdminOrder.js │ │ ├── useAdminProduct.js │ │ ├── useAffiliateEarnings.js │ │ ├── useAuth.js │ │ ├── useAutoFocus.js │ │ ├── useBackendApi.js │ │ ├── useCollection.js │ │ ├── useCollections.js │ │ ├── useConfig.js │ │ ├── useCurrencyOpts.js │ │ ├── useDashboardStats.js │ │ ├── useDiscount.js │ │ ├── useDiscounts.js │ │ ├── useEmailAppsList.js │ │ ├── useFlattenedShippingZones.js │ │ ├── useForm.js │ │ ├── useIsMobile.js │ │ ├── useIsMounted.js │ │ ├── useListingData.js │ │ ├── useMakeOffer.js │ │ ├── useOfferData.js │ │ ├── useOrder.js │ │ ├── useOrders.js │ │ ├── useOrigin.js │ │ ├── usePGP.js │ │ ├── usePaginate.js │ │ ├── usePayment.js │ │ ├── usePaymentDiscount.js │ │ ├── usePrice.js │ │ ├── usePrintful.js │ │ ├── usePrintfulIds.js │ │ ├── useProduct.js │ │ ├── useProducts.js │ │ ├── useRedirect.js │ │ ├── useRest.js │ │ ├── useSearchQuery.js │ │ ├── useSentry.js │ │ ├── useSetState.js │ │ ├── useShipping.js │ │ ├── useShippingZones.js │ │ ├── useShop.js │ │ ├── useShopConfig.js │ │ ├── useShopDeployments.js │ │ ├── useShops.js │ │ ├── useSingleProduct.js │ │ ├── useStripe.js │ │ ├── useThemeVars.js │ │ ├── useThemes.js │ │ ├── useToken.js │ │ ├── useTokenDataProviders.js │ │ └── useWallet.js ├── translation │ ├── README.md │ ├── crowdin │ │ ├── all-messages.json │ │ ├── all-messages_af_ZA.json │ │ ├── all-messages_ar_SA.json │ │ ├── all-messages_ca_ES.json │ │ ├── all-messages_cs_CZ.json │ │ ├── all-messages_da_DK.json │ │ ├── all-messages_de_DE.json │ │ ├── all-messages_el_GR.json │ │ ├── all-messages_en_US.json │ │ ├── all-messages_es_ES.json │ │ ├── all-messages_fi_FI.json │ │ ├── all-messages_fr_FR.json │ │ ├── all-messages_he_IL.json │ │ ├── all-messages_hu_HU.json │ │ ├── all-messages_id_ID.json │ │ ├── all-messages_it_IT.json │ │ ├── all-messages_ja_JP.json │ │ ├── all-messages_ko_KR.json │ │ ├── all-messages_nl_NL.json │ │ ├── all-messages_no_NO.json │ │ ├── all-messages_pl_PL.json │ │ ├── all-messages_pt_BR.json │ │ ├── all-messages_pt_PT.json │ │ ├── all-messages_ro_RO.json │ │ ├── all-messages_ru_RU.json │ │ ├── all-messages_sr_SP.json │ │ ├── all-messages_sv_SE.json │ │ ├── all-messages_tr_TR.json │ │ ├── all-messages_uk_UA.json │ │ ├── all-messages_vi_VN.json │ │ ├── all-messages_zh_CN.json │ │ └── all-messages_zh_TW.json │ └── fbt │ │ ├── .enum_manifest.json │ │ ├── .gitignore │ │ └── README.md └── webpack.config.js └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | **/node_modules/* 2 | **/build/* 3 | **/dist/* 4 | -------------------------------------------------------------------------------- /.eslintrc.react.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('./.eslintrc.js') 2 | 3 | module.exports = { 4 | ...baseConfig, 5 | globals: {}, 6 | extends: [...baseConfig.extends, 'plugin:react/recommended'], 7 | plugins: ['react'], 8 | rules: { 9 | ...baseConfig.rules, 10 | 'react/no-deprecated': 'off', 11 | 'react/no-children-prop': 'off', 12 | 'react/prop-types': 'off', 13 | camelcase: ['error', { properties: 'never' }] 14 | }, 15 | settings: { 16 | react: { 17 | version: 'detect' 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.gitbook.yaml: -------------------------------------------------------------------------------- 1 | root: ./docs 2 | -------------------------------------------------------------------------------- /.gitbook/assets/listing_transaction_flow-listing_transaction_flow_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/.gitbook/assets/listing_transaction_flow-listing_transaction_flow_2.png -------------------------------------------------------------------------------- /.github/workflows/snyk.yml: -------------------------------------------------------------------------------- 1 | name: Snyk push gate 2 | on: push 3 | jobs: 4 | security: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@master 8 | - name: Run Snyk to check for vulnerabilities 9 | uses: snyk/actions/node@master 10 | continue-on-error: true 11 | env: 12 | SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} 13 | with: 14 | args: --severity-threshold=high --all-projects -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.snyk: -------------------------------------------------------------------------------- 1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. 2 | version: v1.19.0 3 | # ignores vulnerabilities until expiry date; change duration by modifying expiry date 4 | ignore: 5 | SNYK-JS-MOCHA-561476: 6 | - '*': 7 | reason: None Given 8 | expires: 2021-08-13T18:29:13.316Z 9 | created: 2021-07-14T18:29:13.320Z 10 | patch: {} 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## dshop/v1.4.0 (2020-08-18) 4 | 5 | - Printful integration 6 | - Localization to Spanish 7 | - Various bug fixes 8 | 9 | ## dshop/v1.3.0 (2020-08-12) 10 | 11 | - Localization support 12 | 13 | ## dshop/v1.2.0 (2020-08-11) 14 | 15 | - Updated shipping UI 16 | 17 | ## dshop/v1.1.0 (2020-08-04) 18 | 19 | - PayPal payment method 20 | 21 | ## dshop/v1.0.1 (2020-08-02) 22 | 23 | - Forgot password 24 | 25 | ## dshop/v1.0.0 (2020-07-30) 26 | 27 | - Initial public release 28 | 29 | ## dshop/v0.9.1 (2020-07-29) 30 | 31 | - Merchant currency selection 32 | 33 | ## dshop/v0.9.0 (2020-07-27) 34 | 35 | - Google Analytics 36 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - 'Publish' modal should show resulting URL and IPFS link 2 | - 'No signer' on metamask 3 | 4 | - Hide preview switcher 5 | - Put logo text in advanced settings 6 | - 'Preview' should open storefront in new tab 7 | 8 | - New merchant signup 9 | - Fix user settings page 10 | - Fix up Uphold payment method 11 | - Validate listing exists / is owner on create shop 12 | -------------------------------------------------------------------------------- /TRANSLATION.md: -------------------------------------------------------------------------------- 1 | # Translation 2 | Note: Most of the code related to translation is (shamelessly) copied over from [origin repo](https://github.com/originprotocol/origin). 3 | 4 | ## Adding a new language 5 | 6 | 1. Update `shop/scripts/crowdinToFbt.js` file, line number 9, to add the new locale to the `locales` array 7 | 2. Create a new file `shop/translation/crowding/all-messages_{{NEW_LOCALE_HERE}}.json` with the contents of `shop/translation/crowding/all-messages.json` 8 | 3. Run `npm run translate` 9 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | db/*.db 4 | data/ 5 | themes/* 6 | !themes/.gitignore -------------------------------------------------------------------------------- /backend/.prettierignore: -------------------------------------------------------------------------------- 1 | dist/* 2 | themes/* 3 | docs/* 4 | nodes_modules/* 5 | db/* 6 | data/* -------------------------------------------------------------------------------- /backend/Procfile: -------------------------------------------------------------------------------- 1 | web: node index.js -------------------------------------------------------------------------------- /backend/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Origin Dshop", 3 | "description": "A decentralized e-commerce store.", 4 | "repository": "https://github.com/OriginProtocol/dshop", 5 | "logo": "https://www.originprotocol.com/static/img/origin-icon.png", 6 | "keywords": ["ethereum", "nodejs", "ipfs"], 7 | "addons": [ 8 | { 9 | "plan": "heroku-postgresql:hobby-dev" 10 | }, 11 | { 12 | "plan": "heroku-redis:hobby-dev" 13 | } 14 | ], 15 | "env": { 16 | "ENCRYPTION_KEY": { 17 | "description": "DB Encryption Key", 18 | "generator": "secret", 19 | "required": true 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /backend/config/README.md: -------------------------------------------------------------------------------- 1 | Directory containing shop configuration templates and default configuration files. 2 | 3 | Used by the back-end logic during a new shop creation. -------------------------------------------------------------------------------- /backend/config/templates/affiliate/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "isAffiliate": true 3 | } 4 | -------------------------------------------------------------------------------- /backend/config/templates/empty/collections.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /backend/config/templates/empty/config.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /backend/config/templates/empty/products.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /backend/config/templates/multi-product/collections.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "shirts", 4 | "title": "Shirts", 5 | "products": ["origin-logo-tee", "cut-out-the-middleman-baseball-tee"] 6 | }, 7 | { 8 | "id": "accessories", 9 | "title": "Accessories", 10 | "products": ["origin-coffee-mug"] 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /backend/config/templates/multi-product/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "byline": "Free shipping on all orders! 🚚💨" 3 | } 4 | -------------------------------------------------------------------------------- /backend/config/templates/multi-product/cut-out-the-middleman-baseball-tee/520/img-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/backend/config/templates/multi-product/cut-out-the-middleman-baseball-tee/520/img-0.png -------------------------------------------------------------------------------- /backend/config/templates/multi-product/cut-out-the-middleman-baseball-tee/orig/img-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/backend/config/templates/multi-product/cut-out-the-middleman-baseball-tee/orig/img-0.png -------------------------------------------------------------------------------- /backend/config/templates/multi-product/origin-coffee-mug/520/img-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/backend/config/templates/multi-product/origin-coffee-mug/520/img-0.png -------------------------------------------------------------------------------- /backend/config/templates/multi-product/origin-coffee-mug/520/img-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/backend/config/templates/multi-product/origin-coffee-mug/520/img-1.png -------------------------------------------------------------------------------- /backend/config/templates/multi-product/origin-coffee-mug/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "origin-coffee-mug", 3 | "title": "Origin Coffee Mug", 4 | "description": "11 oz Origin Coffee Mug\n\nDishwasher and microwave safe", 5 | "price": 2500, 6 | "available": true, 7 | "images": ["img-0.png", "img-1.png"], 8 | "image": "img-0.png", 9 | "variants": [ 10 | { 11 | "id": 0, 12 | "title": "Origin Coffee Mug", 13 | "option1": null, 14 | "option2": null, 15 | "option3": null, 16 | "image": "img-0.png", 17 | "available": true, 18 | "name": "Origin Coffee Mug", 19 | "options": [], 20 | "price": 2500 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/config/templates/multi-product/origin-coffee-mug/orig/img-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/backend/config/templates/multi-product/origin-coffee-mug/orig/img-0.png -------------------------------------------------------------------------------- /backend/config/templates/multi-product/origin-coffee-mug/orig/img-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/backend/config/templates/multi-product/origin-coffee-mug/orig/img-1.png -------------------------------------------------------------------------------- /backend/config/templates/multi-product/origin-logo-tee/520/img-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/backend/config/templates/multi-product/origin-logo-tee/520/img-0.png -------------------------------------------------------------------------------- /backend/config/templates/multi-product/origin-logo-tee/orig/img-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/backend/config/templates/multi-product/origin-logo-tee/orig/img-0.png -------------------------------------------------------------------------------- /backend/config/templates/multi-product/products.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "origin-logo-tee", 4 | "title": "Origin Logo Tee", 5 | "price": 2000, 6 | "image": "img-0.png" 7 | }, 8 | { 9 | "id": "cut-out-the-middleman-baseball-tee", 10 | "title": "Cut Out The Middleman Baseball Tee", 11 | "price": 2000, 12 | "image": "img-0.png" 13 | }, 14 | { 15 | "id": "origin-coffee-mug", 16 | "title": "Origin Coffee Mug", 17 | "price": 2500, 18 | "image": "img-0.png" 19 | } 20 | ] 21 | -------------------------------------------------------------------------------- /backend/config/templates/multi-product/shipping.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "domestic", 4 | "label": "Free Shipping", 5 | "detail": "Arrives in 7 to 10 days", 6 | "countries": ["US"], 7 | "amount": 0 8 | }, 9 | { 10 | "id": "international", 11 | "label": "Free International Shipping", 12 | "detail": "Arrives in 10 to 14 days", 13 | "amount": 0 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /backend/config/templates/multi-product/style.css: -------------------------------------------------------------------------------- 1 | header h1 img { 2 | width: 8rem; 3 | } 4 | .products .product .pic { 5 | padding-top: 85%; 6 | } 7 | -------------------------------------------------------------------------------- /backend/config/templates/single-product/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "byline": "Free shipping on all orders! 🚚💨", 3 | "footer": "© 2019 Origin Protocol. Learn more about this decentralized e-commerce store here.", 4 | "singleProduct": "origin-coffee-mug" 5 | } 6 | -------------------------------------------------------------------------------- /backend/config/templates/single-product/origin-coffee-mug/520/img-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/backend/config/templates/single-product/origin-coffee-mug/520/img-0.png -------------------------------------------------------------------------------- /backend/config/templates/single-product/origin-coffee-mug/520/img-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/backend/config/templates/single-product/origin-coffee-mug/520/img-1.png -------------------------------------------------------------------------------- /backend/config/templates/single-product/origin-coffee-mug/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "origin-coffee-mug", 3 | "title": "Origin Coffee Mug", 4 | "description": "11 oz Origin Coffee Mug\n\nDishwasher and microwave safe", 5 | "price": 2500, 6 | "available": true, 7 | "images": ["img-0.png", "img-1.png"], 8 | "image": "img-0.png", 9 | "variants": [ 10 | { 11 | "id": 0, 12 | "title": "Origin Coffee Mug", 13 | "option1": null, 14 | "option2": null, 15 | "option3": null, 16 | "image": "img-0.png", 17 | "available": true, 18 | "name": "Origin Coffee Mug", 19 | "options": [], 20 | "price": 2500 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/config/templates/single-product/origin-coffee-mug/orig/img-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/backend/config/templates/single-product/origin-coffee-mug/orig/img-0.png -------------------------------------------------------------------------------- /backend/config/templates/single-product/origin-coffee-mug/orig/img-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/backend/config/templates/single-product/origin-coffee-mug/orig/img-1.png -------------------------------------------------------------------------------- /backend/config/templates/single-product/products.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "origin-coffee-mug", 4 | "title": "Origin Coffee Mug", 5 | "price": 2500, 6 | "image": "img-0.png" 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /backend/config/templates/single-product/shipping.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "domestic", 4 | "label": "Free Shipping", 5 | "detail": "Arrives in 7 to 10 days", 6 | "countries": ["US"], 7 | "amount": 0 8 | }, 9 | { 10 | "id": "international", 11 | "label": "Free International Shipping", 12 | "detail": "Arrives in 10 to 14 days", 13 | "amount": 0 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /backend/config/templates/single-product/style.css: -------------------------------------------------------------------------------- 1 | header h1 img { 2 | width: 8rem; 3 | } 4 | .products .product .pic { 5 | padding-top: 85%; 6 | } 7 | -------------------------------------------------------------------------------- /backend/db/config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | 3 | // DB config for migrations only. 4 | // The config used by the app is under src/models/index.js 5 | 6 | const sqlite = `sqlite:${__dirname}/dshop.db` 7 | const url = process.env.DATABASE_URL || sqlite 8 | 9 | module.exports = { 10 | development: { url, logging: false, define: { underscored: true } }, 11 | test: { url, logging: false, define: { underscored: true } }, 12 | production: { url, logging: false, define: { underscored: true } } 13 | } 14 | -------------------------------------------------------------------------------- /backend/db/migrations/20200226214925-addSessions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.createTable('Session', { 6 | sid: { 7 | type: Sequelize.STRING(36), 8 | primaryKey: true 9 | }, 10 | expires: Sequelize.DATE, 11 | data: Sequelize.TEXT, 12 | createdAt: { 13 | type: Sequelize.DATE, 14 | allowNull: false 15 | }, 16 | updatedAt: { 17 | type: Sequelize.DATE, 18 | allowNull: false 19 | } 20 | }) 21 | }, 22 | 23 | down: queryInterface => { 24 | return queryInterface.dropTable('Session') 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /backend/db/migrations/20200306163357-addShopPassword.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.addColumn('shops', 'password', { 6 | type: Sequelize.STRING 7 | }) 8 | }, 9 | 10 | down: queryInterface => { 11 | return queryInterface.removeColumn('shops', 'password') 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/db/migrations/20200316190719-addOrderStatusStr.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.addColumn('orders', 'status_str', { 6 | type: Sequelize.STRING 7 | }) 8 | }, 9 | 10 | down: queryInterface => { 11 | return queryInterface.removeColumn('orders', 'status_str') 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/db/migrations/20200527204305-alterShopsDataJson.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: queryInterface => { 5 | // For sqlite, the column was already created as JSON. 6 | if (queryInterface.sequelize.options.dialect === 'sqlite') return Promise.resolve() 7 | 8 | return queryInterface.sequelize.query(` 9 | ALTER TABLE orders ALTER COLUMN data TYPE JSONB USING data::json; 10 | `) 11 | }, 12 | down: queryInterface => { 13 | if (queryInterface.sequelize.options.dialect === 'sqlite') return Promise.resolve() 14 | 15 | return queryInterface.sequelize.query(` 16 | ALTER TABLE orders ALTER COLUMN data TYPE TEXT; 17 | `) 18 | } 19 | } -------------------------------------------------------------------------------- /backend/db/migrations/20200527205418-alterExternalOrdersDataJson.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: queryInterface => { 5 | // For sqlite, the column was already created as JSON. 6 | if (queryInterface.sequelize.options.dialect === 'sqlite') return Promise.resolve() 7 | return queryInterface.sequelize.query(` 8 | ALTER TABLE external_orders ALTER COLUMN data TYPE JSONB USING data::json; 9 | `) 10 | }, 11 | down: queryInterface => { 12 | if (queryInterface.sequelize.options.dialect === 'sqlite') return Promise.resolve() 13 | 14 | return queryInterface.sequelize.query(` 15 | ALTER TABLE external_orders ALTER COLUMN data TYPE TEXT; 16 | `) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /backend/db/migrations/20200527205424-alterExternalPaymentsDataJson.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: queryInterface => { 5 | // For sqlite, the column was already created as JSON. 6 | if (queryInterface.sequelize.options.dialect === 'sqlite') return Promise.resolve() 7 | 8 | return queryInterface.sequelize.query(` 9 | ALTER TABLE external_payments ALTER COLUMN data TYPE JSONB USING data::json; 10 | `) 11 | }, 12 | down: queryInterface => { 13 | if (queryInterface.sequelize.options.dialect === 'sqlite') return Promise.resolve() 14 | 15 | return queryInterface.sequelize.query(` 16 | ALTER TABLE external_payments ALTER COLUMN data TYPE TEXT; 17 | `) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /backend/db/migrations/20200629101157-addVerifyEmailField.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | const isSQLite = queryInterface.sequelize.options.dialect === 'sqlite' 6 | 7 | return Promise.all([ 8 | queryInterface.addColumn('sellers', 'email_verified', { 9 | type: Sequelize.BOOLEAN, 10 | defaultValue: false 11 | }), 12 | queryInterface.addColumn('sellers', 'data', { 13 | type: isSQLite ? Sequelize.JSON : Sequelize.JSONB, 14 | defaultValue: {} 15 | }) 16 | ]) 17 | }, 18 | 19 | down: queryInterface => { 20 | return Promise.all([ 21 | queryInterface.removeColumn('sellers', 'email_verified'), 22 | queryInterface.removeColumn('sellers', 'data') 23 | ]) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /backend/db/migrations/20200702115957-addHasChangesCol.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.addColumn('shops', 'has_changes', { 6 | type: Sequelize.BOOLEAN, 7 | defaultValue: false 8 | }) 9 | }, 10 | 11 | down: queryInterface => { 12 | return queryInterface.removeColumn('shops', 'has_changes') 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/db/migrations/20200710060151-alterShopDeployment.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: queryInterface => { 5 | return queryInterface.sequelize.query(` 6 | ALTER TABLE shop_deployments RENAME COLUMN ipfs_gateway TO ipfs_pinner; 7 | `) 8 | }, 9 | down: queryInterface => { 10 | return queryInterface.sequelize.query(` 11 | ALTER TABLE shop_deployments RENAME COLUMN ipfs_pinner TO ipfs_gateway; 12 | `) 13 | } 14 | } -------------------------------------------------------------------------------- /backend/db/migrations/20200715125957-addPublicSignups.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.addColumn('networks', 'public_signups', { 6 | type: Sequelize.BOOLEAN, 7 | defaultValue: false 8 | }) 9 | }, 10 | 11 | down: queryInterface => { 12 | return queryInterface.removeColumn('networks', 'public_signups') 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/db/migrations/20200715221932-addIpfsGatewayToShopDeployments.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.addColumn('shop_deployments', 'ipfs_gateway', { 6 | type: Sequelize.STRING, 7 | }) 8 | }, 9 | 10 | down: queryInterface => { 11 | return queryInterface.removeColumn('shop_deployments', 'ipfs_gateway') 12 | } 13 | } -------------------------------------------------------------------------------- /backend/db/migrations/20200731205137-addPaymentCodeToOrder.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.sequelize.transaction(async () => { 6 | await queryInterface.addColumn('orders', 'payment_code', { type: Sequelize.STRING }) 7 | }) 8 | }, 9 | down: (queryInterface) => { 10 | return queryInterface.sequelize.transaction(async () => { 11 | await queryInterface.removeColumn('orders', 'payment_code') 12 | }) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/db/migrations/20200804043644-addWalletAddressToShop.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.sequelize.transaction(async () => { 6 | await queryInterface.addColumn('shops', 'wallet_address', { type: Sequelize.STRING }) 7 | await queryInterface.addIndex('shops', ['wallet_address']) 8 | }) 9 | }, 10 | down: (queryInterface) => { 11 | return queryInterface.sequelize.transaction(async () => { 12 | await queryInterface.removeColumn('shops', 'wallet_address') 13 | }) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /backend/db/migrations/20200811234518-addEtlJobs.js: -------------------------------------------------------------------------------- 1 | const tableName = 'etl_jobs' 2 | 3 | const { EtlJobStatuses } = require('../../utils/enums') 4 | 5 | module.exports = { 6 | up: (queryInterface, Sequelize) => { 7 | return queryInterface.sequelize.transaction(async () => { 8 | await queryInterface.createTable(tableName, { 9 | id: { 10 | type: Sequelize.INTEGER, 11 | autoIncrement: true, 12 | primaryKey: true 13 | }, 14 | status: Sequelize.ENUM(EtlJobStatuses), 15 | created_at: Sequelize.DATE, 16 | updated_at: Sequelize.DATE 17 | }) 18 | }) 19 | }, 20 | down: queryInterface => { 21 | return queryInterface.sequelize.transaction(() => { 22 | queryInterface.dropTable(tableName) 23 | }) 24 | } 25 | } -------------------------------------------------------------------------------- /backend/db/migrations/20200817211350-addTransationTypePayment.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface) => { 5 | const isSqlite = queryInterface.sequelize.options.dialect === 'sqlite' 6 | if (isSqlite) { 7 | return Promise.resolve() 8 | } 9 | 10 | return queryInterface.sequelize.query(` 11 | ALTER TYPE enum_transactions_type ADD VALUE IF NOT EXISTS 'Payment'; 12 | `) 13 | }, 14 | down: () => { 15 | return Promise.resolve() 16 | } 17 | } -------------------------------------------------------------------------------- /backend/db/migrations/20200827212513-addListingIdToNetworks.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.sequelize.transaction(async () => { 6 | await queryInterface.addColumn('networks', 'listing_id', { type: Sequelize.STRING }) 7 | }) 8 | }, 9 | down: (queryInterface) => { 10 | return queryInterface.sequelize.transaction(async () => { 11 | await queryInterface.removeColumn('networks', 'listing_id') 12 | }) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/db/migrations/20200910055828-addColumnUseMarketplaceToNetwork.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.sequelize.transaction(async () => { 6 | await queryInterface.addColumn('networks', 'use_marketplace', { 7 | type: Sequelize.BOOLEAN, 8 | defaultValue: false // by default disable the use of the marketplace. 9 | }) 10 | }) 11 | }, 12 | down: (queryInterface) => { 13 | return queryInterface.sequelize.transaction(async () => { 14 | await queryInterface.removeColumn('networks', 'use_marketplace') 15 | }) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/db/migrations/20200923070907-update-payment-state.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface) => { 5 | const isSQLite = queryInterface.sequelize.options.dialect === 'sqlite' 6 | 7 | if (isSQLite) return Promise.resolve() 8 | return queryInterface.sequelize.query(` 9 | ALTER TYPE enum_orders_payment_status ADD VALUE IF NOT EXISTS 'Pending'; 10 | `).then(() => queryInterface.sequelize.query(` 11 | ALTER TYPE enum_orders_payment_status ADD VALUE IF NOT EXISTS 'Rejected'; 12 | `)) 13 | }, 14 | 15 | down: () => { 16 | return Promise.resolve() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /backend/db/migrations/20200924113715-add-payment-type.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { OrderPaymentTypes } = require('../../utils/enums') 4 | 5 | module.exports = { 6 | up: (queryInterface, Sequelize) => { 7 | return queryInterface.addColumn('orders', 'payment_type', { 8 | type: Sequelize.ENUM(OrderPaymentTypes) 9 | }) 10 | }, 11 | 12 | down: (queryInterface) => { 13 | return queryInterface.removeColumn('orders', 'payment_type') 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /backend/db/migrations/20201008224340-add-bucket-urls.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.sequelize.transaction(async () => { 6 | await queryInterface.addColumn('shop_deployments', 'bucket_urls', { 7 | type: Sequelize.STRING 8 | }) 9 | await queryInterface.addColumn('shop_deployments', 'bucket_http_urls', { 10 | type: Sequelize.STRING 11 | }) 12 | }) 13 | }, 14 | 15 | down: (queryInterface) => { 16 | return queryInterface.sequelize.transaction(async () => { 17 | await queryInterface.removeColumn('shop_deployments', 'bucket_urls') 18 | await queryInterface.removeColumn('shop_deployments', 'bucket_http_urls') 19 | }) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /backend/db/migrations/20201013034846-addArchivedCol.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.addColumn('orders', 'archived', { 6 | type: Sequelize.BOOLEAN, 7 | defaultValue: false // by default, show everything 8 | }) 9 | }, 10 | down: (queryInterface) => { 11 | return queryInterface.removeColumn('orders', 'archived') 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/db/migrations/20201014164908-add-deployment-uuid.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.sequelize.transaction(async () => { 6 | await queryInterface.addColumn('shop_deployments', 'uuid', { 7 | type: Sequelize.STRING 8 | }) 9 | await queryInterface.addIndex('shop_deployments', ['uuid']) 10 | }) 11 | }, 12 | 13 | down: (queryInterface,) => { 14 | return queryInterface.sequelize.transaction(async () => { 15 | await queryInterface.removeIndex('shop_deployments', ['uuid']) 16 | await queryInterface.removeColumn('shop_deployments', 'uuid') 17 | }) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /backend/db/migrations/20201020191102-add-shop-domain-ip.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.addColumn('shop_domains', 'ip_address', { 6 | type: Sequelize.STRING 7 | }) 8 | }, 9 | down: (queryInterface) => { 10 | return queryInterface.removeColumn('shop_domains', 'ip_address') 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /backend/db/migrations/20201028181012-add-cdn-option.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.addColumn('shops', 'enable_cdn', { 6 | type: Sequelize.BOOLEAN, 7 | defaultValue: false 8 | }) 9 | }, 10 | down: (queryInterface) => { 11 | return queryInterface.removeColumn('shops', 'enable_cdn') 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/db/migrations/20201030222628-add-domain-index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface) => { 5 | return queryInterface.addIndex('shop_domains', ['domain']) 6 | }, 7 | 8 | down: (queryInterface) => { 9 | return queryInterface.removeIndex('shop_domains', ['domain']) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /backend/db/migrations/20201103132608-updateDiscountTypeEnum.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface) => { 5 | const isSqlite = queryInterface.sequelize.options.dialect === 'sqlite' 6 | if (isSqlite) { 7 | return Promise.resolve() 8 | } 9 | 10 | return queryInterface.sequelize.query( 11 | `ALTER TYPE enum_discounts_discount_type ADD VALUE IF NOT EXISTS 'payment'` 12 | ) 13 | }, 14 | down: () => Promise.resolve() 15 | } 16 | -------------------------------------------------------------------------------- /backend/db/migrations/20201103132751-addDiscountDataCol.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | const isPostgres = queryInterface.sequelize.options.dialect === 'postgres' 6 | 7 | return queryInterface.addColumn('discounts', 'data', { 8 | type: isPostgres ? Sequelize.JSONB : Sequelize.JSON 9 | }) 10 | }, 11 | down: (queryInterface) => { 12 | return queryInterface.removeColumn('discounts', 'data') 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/db/migrations/20201105121828-addDiscountCols.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.sequelize.transaction(async () => { 6 | await queryInterface.addColumn('discounts', 'min_cart_value', { 7 | type: Sequelize.INTEGER, 8 | defaultValue: 0 9 | }) 10 | await queryInterface.addColumn('discounts', 'max_discount_value', { 11 | type: Sequelize.INTEGER 12 | }) 13 | }) 14 | 15 | }, 16 | down: (queryInterface) => { 17 | return queryInterface.sequelize.transaction(async () => { 18 | await queryInterface.removeColumn('discounts', 'min_cart_value') 19 | await queryInterface.removeColumn('discounts', 'max_discount_value') 20 | }) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /backend/db/migrations/20201110120605-addExcludeShipping.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.addColumn('discounts', 'exclude_shipping', { 6 | type: Sequelize.BOOLEAN 7 | }) 8 | }, 9 | down: (queryInterface) => { 10 | return queryInterface.removeColumn('discounts', 'exclude_shipping') 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /backend/db/migrations/20201110181336-dbModelCleanUp.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface) => { 5 | return queryInterface.sequelize.transaction(async () => { 6 | await queryInterface.dropTable('external_orders') 7 | await queryInterface.dropTable('old_orders') 8 | await queryInterface.dropTable('shop_deployment_names') 9 | }) 10 | }, 11 | 12 | down: () => Promise.resolve() 13 | } 14 | -------------------------------------------------------------------------------- /backend/db/migrations/20201110195338-add-shop-indexes.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface) => { 5 | return queryInterface.sequelize.transaction(async () => { 6 | await queryInterface.addIndex('shops', ['hostname'], { unique: true }) 7 | await queryInterface.addIndex('shops', ['auth_token'], { unique: true }) 8 | }) 9 | }, 10 | 11 | down: (queryInterface) => { 12 | return queryInterface.sequelize.transaction(async () => { 13 | await queryInterface.removeIndex('shops', ['auth_token']) 14 | await queryInterface.removeIndex('shops', ['hostname']) 15 | }) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/db/migrations/20201111223056-addColumnActiveToEtlShop.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.sequelize.transaction(async () => { 6 | await queryInterface.addColumn('etl_shops', 'active', { type: Sequelize.BOOLEAN, }) 7 | }) 8 | }, 9 | down: (queryInterface) => { 10 | return queryInterface.sequelize.transaction(async () => { 11 | await queryInterface.removeColumn('etl_shops', 'active') 12 | }) 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /backend/db/migrations/20201201125225-addPrintfulSyncing.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.addColumn('shops', 'printful_syncing', { 6 | type: Sequelize.BOOLEAN 7 | }) 8 | }, 9 | down: (queryInterface) => { 10 | return queryInterface.removeColumn('shops', 'printful_syncing') 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /backend/db/migrations/202102141140-addExcludeTaxes.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.addColumn('discounts', 'exclude_taxes', { 6 | type: Sequelize.BOOLEAN 7 | }) 8 | }, 9 | down: (queryInterface) => { 10 | return queryInterface.removeColumn('discounts', 'exclude_taxes') 11 | } 12 | } -------------------------------------------------------------------------------- /backend/index.js: -------------------------------------------------------------------------------- 1 | require('./app') 2 | if (process.env.LISTENER !== 'false') { 3 | const start = require('./listener') 4 | start() 5 | } 6 | -------------------------------------------------------------------------------- /backend/logic/deploy/common.js: -------------------------------------------------------------------------------- 1 | class DuplicateDeploymentError extends Error { 2 | constructor(message) { 3 | super(message) 4 | } 5 | } 6 | 7 | module.exports = { 8 | DuplicateDeploymentError 9 | } 10 | -------------------------------------------------------------------------------- /backend/models/affiliate.js: -------------------------------------------------------------------------------- 1 | module.exports = (sequelize, DataTypes) => { 2 | const Affiliate = sequelize.define( 3 | 'Affiliate', 4 | { 5 | account: DataTypes.STRING, // Checksummed ETH address of the affiliate. 6 | firstName: DataTypes.STRING, 7 | lastName: DataTypes.STRING, 8 | email: DataTypes.STRING 9 | }, 10 | { 11 | underscored: true, 12 | tableName: 'affiliates' 13 | } 14 | ) 15 | return Affiliate 16 | } 17 | -------------------------------------------------------------------------------- /backend/models/etl_job.js: -------------------------------------------------------------------------------- 1 | const { EtlJobStatuses } = require('../utils/enums') 2 | 3 | module.exports = (sequelize, DataTypes) => { 4 | const EtlJob = sequelize.define( 5 | 'EtlJob', 6 | { 7 | status: DataTypes.ENUM(EtlJobStatuses) 8 | }, 9 | { 10 | underscored: true, 11 | tableName: 'etl_jobs' 12 | } 13 | ) 14 | 15 | return EtlJob 16 | } 17 | -------------------------------------------------------------------------------- /backend/models/external_event.js: -------------------------------------------------------------------------------- 1 | const { PrintfulWebhookEvents, ExternalServices } = require('../utils/enums') 2 | 3 | module.exports = (sequelize, DataTypes) => { 4 | const isPostgres = sequelize.options.dialect === 'postgres' 5 | 6 | const ExternalEvent = sequelize.define( 7 | 'ExternalEvent', 8 | { 9 | shopId: DataTypes.INTEGER, 10 | service: DataTypes.ENUM(...Object.values(ExternalServices)), 11 | eventType: DataTypes.ENUM(...Object.values(PrintfulWebhookEvents)), 12 | data: isPostgres ? DataTypes.JSONB : DataTypes.JSON 13 | }, 14 | { 15 | underscored: true, 16 | tableName: 'external_events' 17 | } 18 | ) 19 | 20 | ExternalEvent.associate = function (models) { 21 | ExternalEvent.belongsTo(models.Shop, { 22 | foreignKey: 'shopId' 23 | }) 24 | } 25 | 26 | return ExternalEvent 27 | } 28 | -------------------------------------------------------------------------------- /backend/models/product.js: -------------------------------------------------------------------------------- 1 | module.exports = (sequelize, DataTypes) => { 2 | const isPostgres = sequelize.options.dialect === 'postgres' 3 | 4 | const Product = sequelize.define( 5 | 'Product', 6 | { 7 | productId: DataTypes.STRING, // Product Id from products.json 8 | shopId: DataTypes.INTEGER, // The shop the action was applied to. 9 | stockLeft: DataTypes.INTEGER, // Cumulative stock left 10 | variantsStock: isPostgres ? DataTypes.JSONB : DataTypes.JSON, // Available stock of each variant 11 | createdAt: DataTypes.DATE, 12 | updatedAt: DataTypes.DATE 13 | }, 14 | { 15 | underscored: true, 16 | tableName: 'products' 17 | } 18 | ) 19 | 20 | return Product 21 | } 22 | -------------------------------------------------------------------------------- /backend/models/seller.js: -------------------------------------------------------------------------------- 1 | module.exports = (sequelize, DataTypes) => { 2 | const isSQLite = sequelize.options.dialect === 'sqlite' 3 | 4 | const Seller = sequelize.define( 5 | 'Seller', 6 | { 7 | name: DataTypes.STRING, 8 | email: DataTypes.STRING, 9 | password: DataTypes.STRING, 10 | superuser: DataTypes.BOOLEAN, 11 | emailVerified: { 12 | type: DataTypes.BOOLEAN, 13 | defaultValue: false 14 | }, 15 | data: { 16 | type: isSQLite ? DataTypes.JSON : DataTypes.JSONB, 17 | defaultValue: {} 18 | } 19 | }, 20 | { 21 | underscored: true, 22 | tableName: 'sellers' 23 | } 24 | ) 25 | 26 | Seller.associate = function (models) { 27 | Seller.belongsToMany(models.Shop, { through: models.SellerShop }) 28 | } 29 | 30 | return Seller 31 | } 32 | -------------------------------------------------------------------------------- /backend/models/seller_shop.js: -------------------------------------------------------------------------------- 1 | module.exports = (sequelize, DataTypes) => { 2 | const SellerShop = sequelize.define( 3 | 'SellerShop', 4 | { 5 | sellerId: { 6 | type: DataTypes.INTEGER, 7 | primaryKey: true 8 | }, 9 | shopId: { 10 | type: DataTypes.INTEGER, 11 | primaryKey: true 12 | }, 13 | role: DataTypes.STRING 14 | }, 15 | { 16 | underscored: true, 17 | tableName: 'seller_shop' 18 | } 19 | ) 20 | 21 | SellerShop.associate = function (models) { 22 | SellerShop.belongsTo(models.Shop, { 23 | as: 'shop', 24 | foreignKey: { name: 'shopId' } 25 | }) 26 | SellerShop.belongsTo(models.Seller, { 27 | as: 'seller', 28 | foreignKey: { name: 'sellerId' } 29 | }) 30 | } 31 | 32 | return SellerShop 33 | } 34 | -------------------------------------------------------------------------------- /backend/routes/_makeOffer.js: -------------------------------------------------------------------------------- 1 | const { makeOfferQueue } = require('../queues/queues') 2 | 3 | /** 4 | * req.shop 5 | * req.amount amount in cents 6 | * req.body.data encrypted IPFS data hash 7 | */ 8 | async function makeOffer(req, res) { 9 | const encryptedDataIpfsHash = req.body.data 10 | const { shop, amount, paymentCode, paymentType, paymentStatus } = req 11 | 12 | await makeOfferQueue.add({ 13 | shopId: shop.id, 14 | amount, 15 | encryptedDataIpfsHash, 16 | paymentCode, 17 | paymentType, 18 | paymentStatus 19 | }) 20 | 21 | res.json({ success: true }) 22 | } 23 | 24 | module.exports = makeOffer 25 | -------------------------------------------------------------------------------- /backend/routes/themes.js: -------------------------------------------------------------------------------- 1 | const { getAvailableThemes } = require('../logic/themes') 2 | const { authRole, authSellerAndShop } = require('./_auth') 3 | const uploadFiles = require('../logic/shop/upload') 4 | 5 | module.exports = function (router) { 6 | /** 7 | * Returns a list of available themes, 8 | * Requires a server restart to pick up changes 9 | */ 10 | router.get('/themes', (req, res) => { 11 | res.json({ 12 | success: true, 13 | data: getAvailableThemes() 14 | }) 15 | }) 16 | 17 | router.post( 18 | '/themes/upload-images', 19 | authSellerAndShop, 20 | authRole('admin'), 21 | async (req, res) => { 22 | const { status, ...data } = await uploadFiles(req, 'uploads') 23 | res.status(status).send(data) 24 | } 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /backend/scripts/autoVariant.js: -------------------------------------------------------------------------------- 1 | function variants(product, key, values) { 2 | const variants = values.map((v, idx) => { 3 | return { 4 | id: idx, 5 | title: product.title, 6 | option1: v, 7 | option2: null, 8 | option3: null, 9 | image: product.image, 10 | available: true, 11 | options: [v], 12 | price: product.price 13 | } 14 | }) 15 | return JSON.stringify({ ...product, variants }, null, 2) 16 | } 17 | 18 | module.exports = variants 19 | -------------------------------------------------------------------------------- /backend/scripts/aws/marketplace/README.md: -------------------------------------------------------------------------------- 1 | # Dshop AWS EC2 AMI builder 2 | 3 | dshops 4 | 5 | ## Quickstart 6 | 7 | You will need [packer](https://www.packer.io/downloads/) installed, and 8 | [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html#cliv2-linux-install) installed and configured. 9 | 10 | ./build.sh 11 | 12 | ## Authentication 13 | 14 | Packer will need configuration to access AWS. The simplest method is to run `aws configre` and setup your credentials through that. If you'd like to [use another method, see the Packer documentation](https://www.packer.io/docs/builders/amazon#specifying-amazon-credentials). 15 | -------------------------------------------------------------------------------- /backend/scripts/createListing.js: -------------------------------------------------------------------------------- 1 | const { Network } = require('../models') 2 | const { createListing } = require('../utils/createListing') 3 | const contractsJSON = require('../../packages/contracts/build/contracts.json') 4 | 5 | const pk = '0xAE6AE8E5CCBFB04590405997EE2D52D2B330726137B875053C36D94E974D162F' 6 | 7 | async function go() { 8 | const network = await Network.findOne({ where: { networkId: '999' } }) 9 | if (!network.marketplaceContract) { 10 | network.marketplaceContract = contractsJSON.Marketplace_V01 11 | } 12 | const listingId = await createListing({ 13 | network, 14 | pk, 15 | listing: { title: 'Test Shop' } 16 | }) 17 | console.log(`Created listing ${listingId}`) 18 | } 19 | 20 | go() 21 | -------------------------------------------------------------------------------- /backend/scripts/gcp/marketplace/GCP_Deployment_Guide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/backend/scripts/gcp/marketplace/GCP_Deployment_Guide.pdf -------------------------------------------------------------------------------- /backend/scripts/printful/getProductIds.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | 3 | async function getProductIds({ OutputDir }) { 4 | const productsRaw = fs.readFileSync(`${OutputDir}/printful-products.json`) 5 | const products = JSON.parse(productsRaw) 6 | return products.map((p) => p.id) 7 | } 8 | 9 | module.exports = getProductIds 10 | -------------------------------------------------------------------------------- /backend/scripts/printful2.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | // Run printful operations on local data. Mostly useful for testing. 4 | 5 | require('dotenv').config() 6 | const program = require('commander') 7 | const fs = require('fs') 8 | const socket = require('socket.io') 9 | 10 | const writeProductData = require('../logic/printful/sync/writeProductData') 11 | const getSizeGuide = require('./printful/getSizeGuide') 12 | 13 | program.requiredOption('-s, --site ', 'Site name') 14 | 15 | if (!process.argv.slice(2).length) { 16 | program.outputHelp() 17 | process.exit(1) 18 | } 19 | 20 | program.parse(process.argv) 21 | 22 | const OutputDir = `${__dirname}/../data/${program.site}` 23 | 24 | async function start() { 25 | await writeProductData({ OutputDir, png: program.png }) 26 | } 27 | 28 | start() 29 | -------------------------------------------------------------------------------- /backend/sentry.js: -------------------------------------------------------------------------------- 1 | const Sentry = require('@sentry/node') 2 | const sentryEventPrefix = 3 | 'https://sentry.io/organizations/origin-protocol/projects/dshop-backend/events' 4 | 5 | const { getLogger } = require('./utils/logger') 6 | const log = getLogger('sentry') 7 | 8 | const dsn = process.env.SENTRY_DSN 9 | const environment = process.env.ENVIRONMENT || 'development' 10 | 11 | if (dsn) { 12 | log.info(`Initializing Sentry with environment: ${environment}`) 13 | Sentry.init({ dsn, environment }) 14 | } else { 15 | if (environment === 'development') { 16 | log.info('Skipping Sentry initialization') 17 | } else { 18 | log.error('Sentry initialization failed. SENTRY_DSN is not defined.') 19 | } 20 | } 21 | 22 | module.exports = { 23 | Sentry, 24 | sentryEventPrefix 25 | } 26 | -------------------------------------------------------------------------------- /backend/test/README.md: -------------------------------------------------------------------------------- 1 | # Testing Documentation 2 | 3 | ## Environment Variables 4 | 5 | These are the available environment variables: 6 | 7 | - `LOG` or `LOG_LEVEL` - Default is 'INFO'. Can be one of DEBUG, ERROR, LOG, INFO, NONE. [See more.](../utils/logger.js) 8 | 9 | -------------------------------------------------------------------------------- /backend/test/dns.utils.test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai') 2 | const expect = chai.expect 3 | 4 | const { 5 | isPublicDNSName, 6 | isUnstoppableName, 7 | isCryptoName 8 | } = require('@origin/utils/dns') 9 | 10 | const { TEST_DOMAIN_1, TEST_UNSTOPPABLE_DOMAIN_1 } = require('./const') 11 | 12 | describe('DNS Utils', () => { 13 | it('should recognize an Unstoppable domain', async () => { 14 | expect(isUnstoppableName(TEST_UNSTOPPABLE_DOMAIN_1)).to.be.true 15 | }) 16 | 17 | it('should recognize crypto domains', async () => { 18 | expect(isCryptoName(TEST_UNSTOPPABLE_DOMAIN_1)).to.be.true 19 | }) 20 | 21 | it('should recognize non-crypto domains', async () => { 22 | expect(isPublicDNSName(TEST_DOMAIN_1)).to.be.true 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /backend/test/http.utils.test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai') 2 | const expect = chai.expect 3 | 4 | const { getHTTPS } = require('../utils/http') 5 | 6 | const { TEST_DOMAIN_1, BAD_DOMAIN_1 } = require('./const') 7 | 8 | describe('HTTP Utils', () => { 9 | it('should make a successful HTTPS GET request to a known domain', async () => { 10 | expect(await getHTTPS(`https://www.${TEST_DOMAIN_1}`)).to.be.undefined 11 | }) 12 | 13 | it('should fail at making an HTTPS GET request to a non-existant domain', async () => { 14 | return getHTTPS(`https://www.${BAD_DOMAIN_1}`).catch((err) => 15 | expect(err) 16 | .to.be.an('error') 17 | .with.property('message') 18 | .that.has.string('ENOTFOUND') 19 | ) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /backend/test/shop.util.test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai') 2 | const expect = chai.expect 3 | 4 | const { getShopDataUrl } = require('../utils/shop') 5 | 6 | describe('Shop Utils', () => { 7 | it('should create a data URL', async () => { 8 | // Prod environment. 9 | const netConfig = { 10 | domain: 'ogn.app' 11 | } 12 | const shop = { 13 | hostname: 'foo', 14 | authToken: 'bar' 15 | } 16 | let dataUrl = getShopDataUrl(shop, netConfig) 17 | expect(dataUrl).to.be.equal('https://foo.ogn.app/bar/') 18 | 19 | // Dev environment. 20 | netConfig.domain = 'localhost' 21 | netConfig.backendUrl = 'http://localhost:1234' 22 | dataUrl = getShopDataUrl(shop, netConfig) 23 | expect(dataUrl).to.be.equal('http://localhost:1234/bar/') 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /backend/utils/address.js: -------------------------------------------------------------------------------- 1 | const Web3 = require('web3') 2 | 3 | const { NETWORK_ID, CONTRACTS } = require('./const') 4 | 5 | const VERSION_TO_ADDRESS = CONTRACTS[NETWORK_ID].marketplace 6 | const ADDRESS_TO_VERSION = {} 7 | 8 | Object.keys(CONTRACTS[NETWORK_ID].marketplace).map((k) => { 9 | const v = CONTRACTS[NETWORK_ID].marketplace[k] 10 | return (ADDRESS_TO_VERSION[v] = k) 11 | }) 12 | 13 | function addressToVersion(addr) { 14 | const normAddr = Web3.utils.toChecksumAddress(addr) 15 | return ADDRESS_TO_VERSION[normAddr] 16 | } 17 | 18 | function versionToAddress(vers) { 19 | return VERSION_TO_ADDRESS[vers] 20 | } 21 | 22 | function normalizeAddress(addr) { 23 | return Web3.utils.toChecksumAddress(addr) 24 | } 25 | 26 | module.exports = { 27 | addressToVersion, 28 | versionToAddress, 29 | normalizeAddress 30 | } 31 | -------------------------------------------------------------------------------- /backend/utils/array.js: -------------------------------------------------------------------------------- 1 | const difference = require('lodash/difference') 2 | const { assert } = require('./validators') 3 | 4 | /** 5 | * Check if all alements in one array are in another and vice-versa 6 | * 7 | * @param a {array} 8 | * @param b {array} 9 | * @returns {boolean} - if arrays have the same elements 10 | */ 11 | function allIn(a, b) { 12 | assert(a instanceof Array, 'a is not an array') 13 | assert(b instanceof Array, 'b is not an array') 14 | 15 | if (a.length !== b.length) { 16 | return false 17 | } 18 | 19 | return difference(a, b).length === 0 20 | } 21 | 22 | module.exports = { allIn } 23 | -------------------------------------------------------------------------------- /backend/utils/cartData.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch') 2 | 3 | async function fetchItem(dataURL, item) { 4 | const url = `${dataURL}${item.product}/data.json` 5 | const dataRaw = await fetch(url) 6 | const data = await dataRaw.json() 7 | return { 8 | ...item, 9 | product: data, 10 | variant: data.variants.find((v) => v.id === item.variant) 11 | } 12 | } 13 | 14 | async function fetchItems(dataURL, items) { 15 | return await Promise.all(items.map((i) => fetchItem(dataURL, i))) 16 | } 17 | 18 | module.exports = fetchItems 19 | -------------------------------------------------------------------------------- /backend/utils/emails/templates/_orderItem.js: -------------------------------------------------------------------------------- 1 | module.exports = (vars) => ` 2 | 3 | 4 | ${vars.img ? `` : ''} 5 | 6 | ${vars.title} × ${vars.quantity}${vars.options || ''} 7 | ${vars.price} 8 | 9 | ` 10 | -------------------------------------------------------------------------------- /backend/utils/emails/templates/_orderItemTxt.js: -------------------------------------------------------------------------------- 1 | module.exports = (vars) => 2 | `${vars.title} × ${vars.quantity}: ${vars.price}${vars.options}` 3 | -------------------------------------------------------------------------------- /backend/utils/emails/templates/forgotPassEmailTxt.js: -------------------------------------------------------------------------------- 1 | module.exports = (vars) => ` 2 | Hi, ${vars.name}. 3 | 4 | You’re receiving this message because you recently attempted to reset your password. 5 | 6 | Please click below to choose a new password: 7 | 8 | ${vars.verifyUrl} 9 | 10 | This link will expire in 24 hours. If you have questions, we’re here to help: ${vars.supportEmailPlain} 11 | ` 12 | -------------------------------------------------------------------------------- /backend/utils/emails/templates/printfulOrderFailedTxt.js: -------------------------------------------------------------------------------- 1 | module.exports = (vars) => ` 2 | ${vars.siteName} 3 | 4 | You’re receiving this message because one of the orders couldn't be created on Printful because of the following reason: 5 | 6 | > ${vars.message} 7 | 8 | View order on Dshop: ${vars.orderUrlAdmin} 9 | 10 | If you have questions, we’re here to help: ${vars.supportEmail} 11 | ` 12 | -------------------------------------------------------------------------------- /backend/utils/emails/templates/sellerContact.js: -------------------------------------------------------------------------------- 1 | module.exports = (vars) => ` 2 | 3 | ${vars.head} 4 | 5 | 6 | 7 | Hi, ${vars.sellerName} 8 | 9 | ${vars.fullName} (${vars.userEmail}) has sent you the following message from your shop: 10 | 11 | 12 | Subject: ${vars.subject} 13 | 14 | 15 | Message Content: ${vars.content} 16 | 17 | 18 | 19 | 20 | 21 | ` 22 | -------------------------------------------------------------------------------- /backend/utils/emails/templates/sellerContactTxt.js: -------------------------------------------------------------------------------- 1 | module.exports = (vars) => ` 2 | Hi, ${vars.sellerName}. 3 | 4 | ${vars.fullName} (${vars.userEmail}) has sent you the following message from your shop: 5 | 6 | Subject: ${vars.subject} 7 | 8 | Message Content: 9 | ${vars.content} 10 | ` 11 | -------------------------------------------------------------------------------- /backend/utils/emails/templates/stripeWebhookError.js: -------------------------------------------------------------------------------- 1 | module.exports = (vars) => ` 2 | 3 | ${vars.head} 4 | 5 | 6 | 7 | ${vars.siteName} 8 | 9 | You’re receiving this email because following error occured while trying to process a Stripe webhook event: 10 | 11 | 12 | ${vars.message} 13 | 14 | 15 | External Payment ID: ${vars.externalPaymentId} 16 | 17 |
18 | ${vars.stackTrace} 19 |
20 |
21 |
22 | 23 |
24 |
25 | ` 26 | -------------------------------------------------------------------------------- /backend/utils/emails/templates/stripeWebhookErrorTxt.js: -------------------------------------------------------------------------------- 1 | module.exports = (vars) => ` 2 | ${vars.siteName} 3 | 4 | You’re receiving this email because following error occured while trying to process a Stripe webhook event: 5 | 6 | > ${vars.message} 7 | 8 | External Payment ID: ${vars.externalPaymentId} 9 | 10 | ============================ 11 | ${vars.stackTrace} 12 | ============================ 13 | ` 14 | -------------------------------------------------------------------------------- /backend/utils/emails/templates/verifyEmailTxt.js: -------------------------------------------------------------------------------- 1 | module.exports = (vars) => ` 2 | Welcome to Origin Dshop, ${vars.name}. 3 | 4 | You’re receiving this message because you recently signed up for a Dshop account. 5 | 6 | Confirm your email address by clicking the link below. This step adds extra security to your business by verifying you own this email. 7 | 8 | ${vars.verifyUrl} 9 | 10 | This link will expire in 24 hours. If you have questions, we’re here to help: ${vars.supportEmailPlain} 11 | ` 12 | -------------------------------------------------------------------------------- /backend/utils/logger.js: -------------------------------------------------------------------------------- 1 | const Logger = require('logplease') 2 | 3 | const ROOT_NAME = 'dshop' 4 | 5 | if (typeof process.env.LOG === 'undefined') { 6 | Logger.setLogLevel(process.env.LOG_LEVEL || 'INFO') 7 | } 8 | 9 | const isProd = process.env.NODE_ENV === 'production' 10 | 11 | function getLogger(name) { 12 | name = name ? `${ROOT_NAME}.${name}` : ROOT_NAME 13 | 14 | return Logger.create(name, { 15 | showTimestamp: !isProd, 16 | useColors: !isProd 17 | }) 18 | } 19 | 20 | const rootLogger = getLogger() 21 | 22 | module.exports = { 23 | getLogger, 24 | rootLogger 25 | } 26 | -------------------------------------------------------------------------------- /backend/utils/string.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Append e to s 3 | * 4 | * @param {string} s string we're appending to 5 | * @param {string} e string we're appending 6 | * @returns {string} s+e 7 | */ 8 | function append(s, e) { 9 | if (s.endsWith(e)) return s 10 | return `${s}${e}` 11 | } 12 | 13 | module.exports = { 14 | append 15 | } 16 | -------------------------------------------------------------------------------- /crowdin.yml: -------------------------------------------------------------------------------- 1 | files: 2 | - source: /shop/translation/crowdin/all-messages.json 3 | translation: >- 4 | /shop/translation/crowdin/all-messages_%locale_with_underscore%.json 5 | -------------------------------------------------------------------------------- /devops/dockerfiles/cert-issuer-ingress: -------------------------------------------------------------------------------- 1 | FROM openresty/openresty:alpine-fat 2 | 3 | ENV AUTO_SSL_VERSION='0.13.1' 4 | 5 | RUN apk --no-cache add bash openssl \ 6 | && /usr/local/openresty/luajit/bin/luarocks install lua-resty-auto-ssl $AUTO_SSL_VERSION \ 7 | # Remove nginx default config 8 | && rm /etc/nginx/conf.d/default.conf 9 | 10 | COPY ./devops/dockerfiles/files/issuer-nginx.conf.template \ 11 | /usr/local/openresty/nginx/conf/nginx.conf.template 12 | COPY ./devops/dockerfiles/files/issuer-entrypoint.sh /entrypoint.sh 13 | 14 | VOLUME /etc/resty-auto-ssl 15 | 16 | ENTRYPOINT ["/entrypoint.sh"] 17 | CMD ["/usr/local/openresty/bin/openresty", "-g", "daemon off;"] 18 | -------------------------------------------------------------------------------- /devops/helm/charts/dshop/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /devops/helm/charts/dshop/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: dshop 3 | description: A Helm chart for Dshop node 4 | type: application 5 | version: 0.1.0 6 | -------------------------------------------------------------------------------- /devops/helm/charts/dshop/templates/dshop.balancer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ template "dshop.fullname" . }}-balancer 5 | labels: 6 | {{- include "dshop.selectorLabels" . | nindent 8 }} 7 | spec: 8 | type: LoadBalancer 9 | selector: 10 | {{- include "dshop.selectorLabels" . | nindent 4 }} 11 | ports: 12 | - name: http 13 | port: 3000 14 | -------------------------------------------------------------------------------- /devops/helm/charts/dshop/templates/dshop.service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "dshop.fullname" . }} 5 | labels: 6 | {{- include "dshop.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "dshop.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /devops/helm/charts/dshop/templates/dshop.serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "dshop.serviceAccountName" . }} 6 | labels: 7 | {{- include "dshop.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /devops/helm/charts/dshop/templates/dshopcache.pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: "{{ template "dshopCacheStorage.fullname" . }}" 5 | spec: 6 | accessModes: 7 | - ReadWriteMany 8 | storageClassName: "" 9 | volumeName: {{ template "dshopCacheStorage.fullname" . }} 10 | resources: 11 | requests: 12 | storage: {{ default "50Gi" .Values.dshopCacheStorageRequestSize }} 13 | -------------------------------------------------------------------------------- /devops/helm/charts/dshop/templates/env.secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: {{ template "dshop.fullname" . }} 5 | labels: 6 | {{- include "dshop.selectorLabels" . | nindent 8 }} 7 | type: Opaque 8 | data: 9 | ENCRYPTION_KEY: {{ required "Set .Values.dshopEncryptionKey" .Values.dshopEncryptionKey | b64enc | quote}} 10 | SESSION_SECRET: {{ required "Set .Values.dshopSessionSecret" .Values.dshopSessionSecret | b64enc | quote}} 11 | DATABASE_URL: {{ required "Set .Values.dshopDatabaseURL" .Values.dshopDatabaseURL | b64enc | quote}} 12 | SENTRY_DSN: {{ required "Set .Values.dshopSentryDSN" .Values.dshopSentryDSN | b64enc | quote}} 13 | -------------------------------------------------------------------------------- /devops/helm/charts/dshop/templates/issuer.svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ template "issuer.fullname" . }} 5 | labels: 6 | {{- include "dshop.selectorLabels" . | nindent 8 }} 7 | spec: 8 | type: LoadBalancer 9 | loadBalancerIP: {{ .Values.dshopIssuerIp }} 10 | selector: 11 | app: {{ template "issuer.fullname" . }} 12 | ports: 13 | - name: http 14 | port: 80 15 | - name: https 16 | port: 443 17 | -------------------------------------------------------------------------------- /devops/helm/charts/dshop/templates/nfs.pv.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | name: {{ template "dshopCacheStorage.fullname" . }} 5 | labels: 6 | {{- include "dshop.selectorLabels" . | nindent 8 }} 7 | spec: 8 | capacity: 9 | storage: {{ default "50Gi" .Values.dshopCacheStorageRequestSize }} 10 | accessModes: 11 | - ReadWriteMany 12 | nfs: 13 | path: {{ .Values.dshopStorageNFSPath }} 14 | server: {{ .Values.dshopStorageIP }} 15 | -------------------------------------------------------------------------------- /devops/helm/charts/dshop/templates/redis.svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ template "dshopRedis.fullname" . }} 5 | labels: 6 | {{- include "dshop.selectorLabels" . | nindent 8 }} 7 | spec: 8 | clusterIP: None 9 | selector: 10 | app: {{ template "dshopRedis.fullname" . }} 11 | ports: 12 | - name: redis 13 | port: 6379 14 | - name: gossip 15 | port: 16379 16 | -------------------------------------------------------------------------------- /devops/helm/charts/dshop/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "dshop.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "dshop.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "dshop.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /devops/helm/values/dshop/default.yaml: -------------------------------------------------------------------------------- 1 | # This is probably not the values file you want to use. It's used 2 | # for linting, and maybe as an example of what you need to define 3 | dshopImage: 4 | repository: "experimental/dshop-backend" 5 | 6 | issuerImage: 7 | repository: "prod/origin-dapp-creator-issuer" 8 | 9 | dshopCacheStorageRequestSize: 1T 10 | dshopIssuerIp: 127.0.0.1 11 | 12 | # Secrets 13 | dshopEncryptionKey: asdf1234 14 | dshopSessionSecret: asdf1234 15 | dshopDatabaseURL: postgresql://127.0.0.1:5432/dshop 16 | dshopSentryDSN: "" 17 | dshopStorageNFSPath: /dshop_cache 18 | disableQueueProcessors: false 19 | -------------------------------------------------------------------------------- /devops/helm/values/dshop/mainnet.yaml: -------------------------------------------------------------------------------- 1 | dshopImage: 2 | repository: gcr.io/origin-214503/dshop/dshop-backend 3 | tag: "mainnet" 4 | 5 | issuerImage: 6 | repository: gcr.io/origin-214503/dshop/cert-issuer-ingress 7 | tag: "20210224172035" 8 | 9 | replicaCount: 6 10 | 11 | dshopCacheStorageRequestSize: 1T 12 | dshopIssuerIp: 35.247.127.81 13 | dshopIssuerReplicas: 2 14 | dshopStorageNFSPath: /dshop_cache 15 | 16 | # Resolver that needs to also see redis. 17 | # Likely .10 on the k8s subnet 18 | issuerNginxResolver: 10.64.0.10 19 | 20 | disableQueueProcessors: true 21 | -------------------------------------------------------------------------------- /devops/helm/values/dshop/rinkeby.yaml: -------------------------------------------------------------------------------- 1 | dshopImage: 2 | repository: gcr.io/origin-214503/dshop/dshop-backend 3 | tag: "latest" 4 | 5 | issuerImage: 6 | repository: gcr.io/origin-214503/dshop/cert-issuer-ingress 7 | tag: "20210225114406" 8 | 9 | dshopCacheStorageRequestSize: 1T 10 | dshopIssuerIp: 34.105.16.10 11 | dshopIssuerReplicas: 2 12 | dshopStorageNFSPath: /dshop_cache 13 | 14 | # Resolver that needs to also see redis. 15 | # Likely .10 on the k8s subnet 16 | issuerNginxResolver: 10.64.0.10 17 | 18 | disableQueueProcessors: false 19 | -------------------------------------------------------------------------------- /devops/helm/values/monitoring/values.yaml: -------------------------------------------------------------------------------- 1 | grafana: 2 | grafana.ini: 3 | smtp: 4 | enabled: true 5 | skip_verify: true 6 | host: smtp.sendgrid.net:2525 7 | from_address: no-reply@originprotocol.com 8 | dashboardsConfigMaps: 9 | default: grafana-dashboards-default 10 | dashboardProviders: 11 | dashboardproviders.yaml: 12 | apiVersion: 1 13 | providers: 14 | - name: default 15 | orgId: 1 16 | folder: '' 17 | type: file 18 | disableDeletion: false 19 | editable: true 20 | options: 21 | path: /var/lib/grafana/dashboards/default 22 | -------------------------------------------------------------------------------- /docs/.gitbook/assets/dshop-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/.gitbook/assets/dshop-flow.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/dshop-offer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/.gitbook/assets/dshop-offer.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/listing_transaction_flow-listing_transaction_flow_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/.gitbook/assets/listing_transaction_flow-listing_transaction_flow_2.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/origin-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/.gitbook/assets/origin-header.png -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # What is Origin Protocol? 2 | 3 | Airbnb, Uber, Craigslist, E-bay, Shopify, Etsy... all of these sites help facilitate transactions between buyers and sellers. A seller lists some goods or services for sale. A buyer makes a purchase from that seller. The marketplace facilitating the transaction usually takes a fee, either from the seller, the buyer, or both. Sometimes the fees are exorbitant, but buyers and sellers are willing to pay in order to obtain access to the network, tools that make buying and selling easy, customer service, insurance, etc. 4 | 5 | Signing up to these services comes at a price, however, and not just the transaction fees. 6 | 7 | -------------------------------------------------------------------------------- /docs/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [What is Origin Protocol?](README.md) 4 | 5 | ## Dshop 6 | 7 | * [Overview](dshop/dshop.md) 8 | * [Front End](dshop/front-end.md) 9 | * [Backend](dshop/backend.md) 10 | * [Order Fulfillment](dshop/order-fulfillment.md) 11 | * [Data Import](dshop/data-import.md) 12 | 13 | ## Protocol 14 | 15 | * [Smart Contract](smart-contract/marketplace-smart-contract.md) 16 | * [Disputes](smart-contract/disputes.md) 17 | * [Commissions](smart-contract/commissions.md) 18 | * [Listings](smart-contract/api.md) 19 | * [Transaction Flow](smart-contract/transaction-flow.md) 20 | 21 | ## Themes 22 | 23 | * [Overview](themes/overview.md) 24 | * [Creating a Theme](themes/create-theme.md) 25 | * [Configurable Fields](themes/config-fields.md) 26 | 27 | -------------------------------------------------------------------------------- /docs/dshop/backend.md: -------------------------------------------------------------------------------- 1 | # Backend 2 | 3 | The backend consists of: 4 | 5 | * A realtime connection to an Ethereum node listening for relevant events on the 6 | 7 | Origin Marketplace contract 8 | 9 | * A database containing up to date customer order information 10 | * An emailer for sending out receipts and notifications 11 | * An off-chain payment processor for accepting credit card orders and putting 12 | 13 | them on-chain 14 | 15 | * An API for sellers to view and manage orders 16 | 17 | The backend can be hosted by any individual or third party provider. 18 | 19 | ## Deployment Guides 20 | 21 | - [Google Cloud Platform (GCP)](deployment/gcp.md) 22 | - [Amazon Web Services (AWS)](deployment/aws.md) 23 | -------------------------------------------------------------------------------- /docs/dshop/data-import.md: -------------------------------------------------------------------------------- 1 | # Data Import 2 | 3 | Dshop comes with data importers for Shopify and Origin Marketplace. This tool makes it easy to migrate an existing Shopify or Origin Marketplace store by importing product data and associated media and restructuring it to a format consumable by Dshop. 4 | 5 | -------------------------------------------------------------------------------- /docs/dshop/deployment/images/aws-accept-terms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/dshop/deployment/images/aws-accept-terms.png -------------------------------------------------------------------------------- /docs/dshop/deployment/images/aws-continue-to-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/dshop/deployment/images/aws-continue-to-config.png -------------------------------------------------------------------------------- /docs/dshop/deployment/images/aws-continue-to-launch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/dshop/deployment/images/aws-continue-to-launch.png -------------------------------------------------------------------------------- /docs/dshop/deployment/images/aws-launch-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/dshop/deployment/images/aws-launch-1.png -------------------------------------------------------------------------------- /docs/dshop/deployment/images/aws-launch-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/dshop/deployment/images/aws-launch-2.png -------------------------------------------------------------------------------- /docs/dshop/deployment/images/aws-launch-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/dshop/deployment/images/aws-launch-3.png -------------------------------------------------------------------------------- /docs/dshop/deployment/images/aws-route53-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/dshop/deployment/images/aws-route53-1.png -------------------------------------------------------------------------------- /docs/dshop/deployment/images/aws-route53-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/dshop/deployment/images/aws-route53-2.png -------------------------------------------------------------------------------- /docs/dshop/deployment/images/aws-route53-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/dshop/deployment/images/aws-route53-3.png -------------------------------------------------------------------------------- /docs/dshop/deployment/images/firstrun-configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/dshop/deployment/images/firstrun-configuration.png -------------------------------------------------------------------------------- /docs/dshop/deployment/images/firstrun-registration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/dshop/deployment/images/firstrun-registration.png -------------------------------------------------------------------------------- /docs/dshop/deployment/images/gcp-deployment-finished.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/dshop/deployment/images/gcp-deployment-finished.png -------------------------------------------------------------------------------- /docs/dshop/deployment/images/gcp-deployment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/dshop/deployment/images/gcp-deployment.png -------------------------------------------------------------------------------- /docs/dshop/deployment/images/gcp-dns-records.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/dshop/deployment/images/gcp-dns-records.png -------------------------------------------------------------------------------- /docs/dshop/deployment/images/gcp-instance-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/dshop/deployment/images/gcp-instance-config.png -------------------------------------------------------------------------------- /docs/dshop/deployment/images/gcp-instance-details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/dshop/deployment/images/gcp-instance-details.png -------------------------------------------------------------------------------- /docs/dshop/deployment/images/gcp-marketplace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/docs/dshop/deployment/images/gcp-marketplace.png -------------------------------------------------------------------------------- /docs/dshop/front-end.md: -------------------------------------------------------------------------------- 1 | # Front End 2 | 3 | The front end consists of a responsive web app built with React and hosted statically on IPFS, centralized server or both. Product data is also stored in IPFS as JSON blobs, along with relevant media such as images or videos. 4 | 5 | -------------------------------------------------------------------------------- /docs/dshop/order-fulfillment.md: -------------------------------------------------------------------------------- 1 | # Order Fulfillment 2 | 3 | Sellers are able to view orders as well as make updates to products via the Admin page. 4 | 5 | -------------------------------------------------------------------------------- /docs/smart-contract/commissions.md: -------------------------------------------------------------------------------- 1 | # Commissions 2 | 3 | A successful marketplace requires both buyer and sellers, but how do buyers find the items they wish to purchase? Marketing and ads are a common solution: a seller will purchase ads or otherwise market their product in the hope a buyer will see it and make a purchase. Paying for ads does not guarantee sales, however. 4 | 5 | The Marketplace takes a different approach by allowing sellers to incentivize affiliates to find buyers on their behalf by offering a commission on sales. This commission is paid once a purchase is complete, so affiliates are incentivized to make sales instead of simply driving traffic. 6 | 7 | -------------------------------------------------------------------------------- /docs/themes/overview.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Themes allow you to customize your Dshop to make it look unique. We provide 4 themes that you can use at the moment. You can also design and develop your own themes to be used on Dshop. 4 | 5 | Dshop also comes with a built-in Theme Editor, you can use it to customize the theme and see the changes in real-time for your Dshop. 6 | 7 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": ["shop/**", "backend/**", "packages/**"], 3 | "npmClient": "yarn", 4 | "useWorkspaces": true, 5 | "version": "independent" 6 | } 7 | -------------------------------------------------------------------------------- /origin-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/origin-header.png -------------------------------------------------------------------------------- /packages/contracts-new/.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity -------------------------------------------------------------------------------- /packages/contracts-new/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | #Buidler files 4 | cache 5 | artifacts 6 | -------------------------------------------------------------------------------- /packages/contracts-new/buidler.config.js: -------------------------------------------------------------------------------- 1 | usePlugin("@nomiclabs/buidler-waffle"); 2 | 3 | // This is a sample Buidler task. To learn how to create your own go to 4 | // https://buidler.dev/guides/create-task.html 5 | task("accounts", "Prints the list of accounts", async () => { 6 | const accounts = await ethers.getSigners(); 7 | 8 | for (const account of accounts) { 9 | console.log(await account.getAddress()); 10 | } 11 | }); 12 | 13 | // You have to export an object to set up your config 14 | // This object can have the following optional entries: 15 | // defaultNetwork, networks, solc, and paths. 16 | // Go to https://buidler.dev/config/ to learn more 17 | module.exports = { 18 | // This is a sample solc configuration that specifies which version of solc to use 19 | solc: { 20 | version: "0.6.8", 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /packages/contracts-new/contracts/ERC20FixedSupplyBurnable.sol: -------------------------------------------------------------------------------- 1 | // contracts/ERC20FixedSupplyBurnable.sol 2 | // SPDX-License-Identifier: MIT 3 | pragma solidity ^0.6.0; 4 | 5 | import '@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol'; 6 | 7 | contract ERC20FixedSupplyBurnable is ERC20Burnable { 8 | constructor( 9 | string memory name, 10 | string memory symbol, 11 | uint256 initialSupply 12 | ) public ERC20(name, symbol) { 13 | _mint(msg.sender, initialSupply); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/contracts-new/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "contracts-new", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@nomiclabs/buidler": "^1.4.8", 13 | "@openzeppelin/contracts": "^3.2.0" 14 | }, 15 | "devDependencies": { 16 | "@nomiclabs/buidler-ethers": "2.0.2", 17 | "@nomiclabs/buidler-waffle": "2.1.0", 18 | "chai": "4.3.0", 19 | "ethereum-waffle": "3.2.2", 20 | "ethers": "5.0.29" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/contracts-new/test/sample-test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require("chai"); 2 | 3 | describe("Greeter", function() { 4 | it("Should return the new greeting once it's changed", async function() { 5 | const Greeter = await ethers.getContractFactory("Greeter"); 6 | const greeter = await Greeter.deploy("Hello, world!"); 7 | 8 | await greeter.deployed(); 9 | expect(await greeter.greet()).to.equal("Hello, world!"); 10 | 11 | await greeter.setGreeting("Hola, mundo!"); 12 | expect(await greeter.greet()).to.equal("Hola, mundo!"); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /packages/contracts/.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /packages/contracts/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | build/* 3 | -------------------------------------------------------------------------------- /packages/contracts/.solcover.js: -------------------------------------------------------------------------------- 1 | require('babel-register') 2 | require('babel-polyfill') 3 | 4 | module.exports = { 5 | copyPackages: ['openzeppelin-solidity'], 6 | testCommand: 'npm run test', 7 | // for Truffle tests (this runs out of gas) 8 | // testCommand: 'truffle test --network coverage', 9 | } 10 | -------------------------------------------------------------------------------- /packages/contracts/.soliumignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | contracts/Migrations.sol 3 | -------------------------------------------------------------------------------- /packages/contracts/.soliumrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solium:recommended", 3 | "plugins": [ 4 | "security" 5 | ], 6 | "rules": { 7 | "security/no-inline-assembly": "off", 8 | "security/no-tx-origin": "off", 9 | "security/no-block-members": "off", 10 | "security/no-low-level-calls": "off", 11 | "security/no-send": "off", 12 | "max-len": "off", 13 | "no-empty-blocks": "off", 14 | "quotes": [ 15 | "error", 16 | "double" 17 | ], 18 | "indentation": [ 19 | "error", 20 | 4 21 | ] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/contracts/README.md: -------------------------------------------------------------------------------- 1 | # ![origin_github_banner](https://user-images.githubusercontent.com/673455/37314301-f8db9a90-2618-11e8-8fee-b44f38febf38.png) 2 | 3 | ## Origin Contracts 4 | 5 | This repo contains the Solidity contracts powering Origin Protocol DShop, 6 | consisting of: 7 | 8 | **OGN Token** Origin Token is built on the ERC20 standard, inheriting from Open 9 | Zeppelin contracts with a few customizations. 10 | 11 | **Marketplace** The marketplace contract facilitates transactions between buyers 12 | and sellers. It supports escrow, arbitration and transactions in Eth and ERC20 13 | tokens, as well as incorporating a commission model and staking in OGN. 14 | -------------------------------------------------------------------------------- /packages/contracts/build/contracts_origin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Marketplace": "0xBFF408fD7DCb4E9B3fF68941699636a86B93eD7C", 3 | "MarketplaceEpoch": 8898, 4 | "Marketplace_V01": "0xcddE3E2D6b42764DB3637b0463D529209526569e", 5 | "MarketplaceEpoch_V01": 137982, 6 | "IdentityEvents": "0xeEEDf6E5f697C1B7D58F13A95b0b65d731A1068B", 7 | "IdentityEventsEpoch": 8794, 8 | "UniswapDaiExchange": "0x8ba2d7842262a16f9522e7397d467e2cb27e4e54", 9 | "ProxyFactory": "0x66654fb666ef2d5e9129242cea3c82b3f9f9d16f", 10 | "ProxyFactoryEpoch": 8405, 11 | "IdentityProxyImplementation": "0x1372a6c7cf0e6c0a8caf05ea32387a4401af77a8", 12 | "OGN": "0xc923fe39022d528889a11738792dd07eac388426", 13 | "DAI": "0x0207a4fc06b9905d0c4fb741b3f6d2d134d00b38" 14 | } 15 | -------------------------------------------------------------------------------- /packages/contracts/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | constructor() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/contracts/contracts/token/MockOUSD.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "../../node_modules/openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol"; 4 | import "../../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol"; 5 | 6 | /** 7 | * This is intended to be used for testing integration between the Marketplace 8 | * and other ERC20 tokens 9 | */ 10 | 11 | contract MockOUSD is StandardToken, Ownable { 12 | string public name; 13 | string public symbol; 14 | uint8 public decimals; 15 | 16 | constructor(string _name, string _symbol, uint8 _decimals, uint _supply) public { 17 | name = _name; 18 | symbol = _symbol; 19 | decimals = _decimals; 20 | totalSupply_ = _supply; 21 | balances[msg.sender] = _supply; 22 | owner = msg.sender; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/contracts/contracts/token/TestToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "../../node_modules/openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol"; 4 | 5 | /** 6 | * This is intended to be used for testing integration between the Marketplace 7 | * and other ERC20 tokens 8 | */ 9 | 10 | contract TestToken is StandardToken { 11 | string public name; 12 | string public symbol; 13 | uint8 public decimals; 14 | 15 | constructor(string _name, string _symbol, uint8 _decimals, uint _supply) public { 16 | name = _name; 17 | symbol = _symbol; 18 | decimals = _decimals; 19 | totalSupply_ = _supply; 20 | balances[msg.sender] = _supply; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/contracts/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /packages/contracts/migrations/README.md: -------------------------------------------------------------------------------- 1 | # Migrations 2 | 3 | These scripts define how contracts are deployed onto the blockchain. 4 | Once a migration has been run, it cannot be undone. Truffle keeps track 5 | of which migrations have already been run (via `Migrations.sol`) and 6 | will only run _new_ migrations. You can force all new migrations with 7 | the `--reset` modifier. ([link](https://truffleframework.com/docs/truffle/getting-started/running-migrations#command)) 8 | -------------------------------------------------------------------------------- /packages/contracts/truffle.js: -------------------------------------------------------------------------------- 1 | // Local setup 2 | const truffleSetup = { 3 | migrations_directory: './migrations', 4 | networks: { 5 | development: { 6 | host: 'localhost', 7 | port: 8545, 8 | network_id: '*' // Match any network id 9 | } 10 | }, 11 | coverage: { 12 | host: 'localhost', 13 | network_id: '*', 14 | port: 8555, // <-- If you change this, also set the port option in .solcover.js. 15 | gas: 0xfffffffffff, // <-- Use this high gas value 16 | gasPrice: 0x01 // <-- Use this low gas price 17 | }, 18 | solc: { optimizer: { enabled: true, runs: 200 } } 19 | } 20 | 21 | // These are needed to use ES2015+ syntax, such as import. The token tests 22 | // imported from OpenZeppelin need these. 23 | require('@babel/register') 24 | require('@babel/polyfill') 25 | 26 | module.exports = truffleSetup 27 | -------------------------------------------------------------------------------- /packages/dshop-validation/index.js: -------------------------------------------------------------------------------- 1 | function assert(cond, message) { 2 | if (!cond) { 3 | throw new Error(`Assertion error: ${message}`) 4 | } 5 | return true 6 | } 7 | 8 | module.exports = { 9 | assert 10 | } 11 | -------------------------------------------------------------------------------- /packages/ipfs/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('../../.eslintrc.js') 2 | 3 | module.exports = { 4 | ...baseConfig, 5 | rules: { 6 | ...baseConfig.rules, 7 | camelcase: ['error', { properties: 'never' }] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/ipfs/README.md: -------------------------------------------------------------------------------- 1 | # Origin IPFS 2 | 3 | Convenience methods for getting and setting data in IPFS 4 | 5 | ## Usage 6 | 7 | ``` 8 | import { get, post } from '@origin/ipfs' 9 | 10 | const ipfsHash = await post("https://ipfs.originprotocol.com", { my: "data" }) 11 | console.log(ipfsHash) 12 | 13 | const retrieved = await get("https://ipfs.originprotocol.com", ipfsHash) 14 | console.log(retrieved) 15 | 16 | ``` 17 | -------------------------------------------------------------------------------- /packages/services/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('../../.eslintrc.js') 2 | 3 | module.exports = { 4 | ...baseConfig, 5 | rules: { 6 | ...baseConfig.rules, 7 | camelcase: ['error', { properties: 'never' }] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/services/.gitignore: -------------------------------------------------------------------------------- 1 | data/db 2 | data/ipfs 3 | data/geth 4 | data/tmp 5 | -------------------------------------------------------------------------------- /packages/services/README.md: -------------------------------------------------------------------------------- 1 | # Origin Services 2 | 3 | Provides Ganache and IPFS services for use when developing locally or for running tests. 4 | 5 | ## Usage 6 | 7 | const services = require('@origin/services') 8 | 9 | const shutdown = await services({ ganache: true, ipfs: true }) 10 | 11 | // Do operations... 12 | 13 | shutdown() 14 | -------------------------------------------------------------------------------- /packages/services/data/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/data/.gitignore -------------------------------------------------------------------------------- /packages/services/fixtures/gift-cards/amazon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/gift-cards/amazon.png -------------------------------------------------------------------------------- /packages/services/fixtures/gift-cards/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/gift-cards/default.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/gift-cards/itunes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/gift-cards/itunes.png -------------------------------------------------------------------------------- /packages/services/fixtures/gift-cards/starbucks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/gift-cards/starbucks.png -------------------------------------------------------------------------------- /packages/services/fixtures/gift-cards/target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/gift-cards/target.png -------------------------------------------------------------------------------- /packages/services/fixtures/gift-cards/walmart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/gift-cards/walmart.png -------------------------------------------------------------------------------- /packages/services/fixtures/hawaii-house/image-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/hawaii-house/image-1.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/hawaii-house/image-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/hawaii-house/image-2.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/hawaii-house/image-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/hawaii-house/image-3.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/hawaii-house/image-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/hawaii-house/image-4.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/hawaii-house/image-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/hawaii-house/image-5.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/lake-house/image-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/lake-house/image-1.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/lake-house/image-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/lake-house/image-2.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/lake-house/image-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/lake-house/image-3.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/lake-house/image-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/lake-house/image-4.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/lake-house/image-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/lake-house/image-5.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/lake-house/image-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/lake-house/image-6.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/offer-valid.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaId": "https://schema.originprotocol.com/offer_1.0.0.json", 3 | "listingId": "999-000-1", 4 | "listingType": "unit", 5 | "unitsPurchased": 1, 6 | "totalPrice": { 7 | "currency": "ETH", 8 | "amount": "0.033" 9 | }, 10 | "commission": { 11 | "currency": "OGN", 12 | "amount": "0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/services/fixtures/origin-spaceman/image-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/origin-spaceman/image-1.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/profile-valid.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaId": "https://schema.originprotocol.com/profile_1.0.0.json", 3 | "firstName": "originus", 4 | "lastName": "protocolus", 5 | "description": "Semper Fidelis", 6 | "avatar": "data:,Avatar" 7 | } 8 | -------------------------------------------------------------------------------- /packages/services/fixtures/review-valid.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaId": "https://schema.originprotocol.com/review_1.0.0.json", 3 | "rating": 3, 4 | "text": "Good stuff" 5 | } -------------------------------------------------------------------------------- /packages/services/fixtures/scout/image-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/scout/image-1.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/scout/image-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/scout/image-2.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/scout/image-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/scout/image-3.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/scout/image-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/scout/image-4.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/taylor-swift-tix/image-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/taylor-swift-tix/image-1.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/taylor-swift-tix/image-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/taylor-swift-tix/image-2.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/taylor-swift-tix/image-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/taylor-swift-tix/image-3.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/taylor-swift-tix/image-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/taylor-swift-tix/image-4.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/taylor-swift-tix/image-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/taylor-swift-tix/image-5.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/zinc-house/image-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/zinc-house/image-1 -------------------------------------------------------------------------------- /packages/services/fixtures/zinc-house/image-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/zinc-house/image-1.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/zinc-house/image-10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/zinc-house/image-10.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/zinc-house/image-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/zinc-house/image-2.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/zinc-house/image-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/zinc-house/image-3.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/zinc-house/image-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/zinc-house/image-4.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/zinc-house/image-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/zinc-house/image-5.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/zinc-house/image-7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/zinc-house/image-7.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/zinc-house/image-8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/zinc-house/image-8.jpg -------------------------------------------------------------------------------- /packages/services/fixtures/zinc-house/image-9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/services/fixtures/zinc-house/image-9.jpg -------------------------------------------------------------------------------- /packages/utils/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('../../.eslintrc.js') 2 | 3 | module.exports = { 4 | ...baseConfig, 5 | rules: { 6 | ...baseConfig.rules, 7 | camelcase: ['error', { properties: 'never' }] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/utils/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/packages/utils/.gitignore -------------------------------------------------------------------------------- /packages/utils/README.md: -------------------------------------------------------------------------------- 1 | #@origin/utils 2 | 3 | All code shared across packages goes in here... -------------------------------------------------------------------------------- /packages/utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Origin Protocol", 3 | "name": "@origin/utils", 4 | "version": "0.1.0", 5 | "license": "MIT", 6 | "description": "Shared code", 7 | "main": "index.js", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/OriginProtocol/dshop" 11 | }, 12 | "scripts": { 13 | "lint": "eslint . && npm run prettier:check", 14 | "prettier": "prettier --write *.js \"**/*.js\"", 15 | "prettier:check": "prettier -c *.js \"**/*.js\"" 16 | }, 17 | "dependencies": { 18 | "lodash": "^4.17.20", 19 | "logplease": "^1.2.15" 20 | }, 21 | "devDependencies": { 22 | "eslint": "7.19.0", 23 | "prettier": "2.2.1" 24 | }, 25 | "prettier": { 26 | "semi": false, 27 | "singleQuote": true, 28 | "proseWrap": "always", 29 | "trailingComma": "none" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /scripts/clean-package-locks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | find . -maxdepth 3 -name package-lock.json -type f -delete 4 | -------------------------------------------------------------------------------- /scripts/test.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const execSync = require('child_process').execSync 3 | 4 | function exec(cmd) { 5 | execSync(cmd, { stdio: 'inherit', env: process.env }) 6 | } 7 | 8 | const cwd = process.cwd() 9 | 10 | ;['shop', 'backend'].forEach( 11 | packageName => { 12 | process.chdir(path.resolve(__dirname, '../' + packageName)) 13 | exec('yarn test') 14 | } 15 | ) 16 | 17 | process.chdir(cwd) 18 | -------------------------------------------------------------------------------- /shop/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('../.eslintrc.react.js') 2 | 3 | module.exports = { 4 | ...baseConfig, 5 | globals: { 6 | ...baseConfig.globals, 7 | openpgp: 'readonly' 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /shop/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | public/* 4 | !public/images* 5 | !public/template.html 6 | !public/template-cdn.html 7 | !public/favicon.ico 8 | /data/* 9 | !/data/example* 10 | backend/scripts/output* 11 | backend/dist 12 | TODO 13 | .env* 14 | 15 | /.enum_manifest.json 16 | /.src_manifest.json 17 | /.source_strings.json 18 | /.translated_fbts.json 19 | -------------------------------------------------------------------------------- /shop/.prettierignore: -------------------------------------------------------------------------------- 1 | docs/* 2 | public/* 3 | node_modules/* 4 | -------------------------------------------------------------------------------- /shop/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Monday April 20th 2020 4 | 5 | - Add createdAt to orders DB 6 | - Admin sales chart 7 | - Admin manually create order from encrypted IPFS hash 8 | - Break out makeOffer in backend 9 | - Discord webhook 10 | - Prettier admin order details 11 | 12 | ## Friday April 17th 2020 13 | 14 | - Optional phone number entry on checkout 15 | - Max order quantity on products 16 | - Move size guide down if too wide 17 | - Footer styling 18 | - Admin Dashboard 19 | - Improved price formatting 20 | - Improved Admin CSV order export 21 | - Fix discounts deletion issue 22 | - HTML Meta description 23 | -------------------------------------------------------------------------------- /shop/buildThemeDev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | export NODE_ENV=development 5 | export THEME=$1 6 | mkdir -p "../backend/themes/$1" 7 | cp "./src/themes/$1/screenshot.png" "../backend/themes/$1/" 8 | cp "./src/themes/$1/theme.json" "../backend/themes/$1/" 9 | ./node_modules/.bin/webpack --config ./webpack.config.js --watch --output-path="../backend/themes/$1" -------------------------------------------------------------------------------- /shop/dev.env: -------------------------------------------------------------------------------- 1 | ENCRYPTION_KEY=randomstring -------------------------------------------------------------------------------- /shop/docs/assets/dshop-flow.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/shop/docs/assets/dshop-flow.afdesign -------------------------------------------------------------------------------- /shop/docs/assets/dshop-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/shop/docs/assets/dshop-flow.png -------------------------------------------------------------------------------- /shop/docs/assets/dshop-offer.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/shop/docs/assets/dshop-offer.afdesign -------------------------------------------------------------------------------- /shop/docs/assets/dshop-offer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/shop/docs/assets/dshop-offer.png -------------------------------------------------------------------------------- /shop/docs/index.md: -------------------------------------------------------------------------------- 1 | # dshop 2 | 3 | dshop is a decentralized e-commerce store served entirely from IPFS. dshop stores can accept credit cards, etherium, and ERC20 coins. 4 | 5 | 6 | 7 | When using cryptocurrencies, the user facing dshop requires no connection to the backend. Loading the site, products, and submitting orders all happens over IPFS and the blockchain. This decoupling also makes for an extremely responsive and user friendly shopping experience. 8 | 9 | Currently there are few docs on working with the dshop. 10 | 11 | Details: 12 | 13 | - [Order Flow](backend/docs/order.md) 14 | 15 | Other docs: 16 | 17 | - [Main README](../README.md) 18 | - [Backend README](backend/README.md) 19 | - [Backend docs](backend/docs/README.md) 20 | 21 | 22 | -------------------------------------------------------------------------------- /shop/public/images/add-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /shop/public/images/affiliate-link-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /shop/public/images/affiliate-sign.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /shop/public/images/aws-ses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/shop/public/images/aws-ses.png -------------------------------------------------------------------------------- /shop/public/images/box.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /shop/public/images/caret-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /shop/public/images/default-image.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /shop/public/images/delete-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /shop/public/images/desktop-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /shop/public/images/discover.svg: -------------------------------------------------------------------------------- 1 | Discover -------------------------------------------------------------------------------- /shop/public/images/edit-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /shop/public/images/error-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /shop/public/images/fire-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /shop/public/images/green-checkmark-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /shop/public/images/green-checkmark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /shop/public/images/info-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /shop/public/images/mailgun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/shop/public/images/mailgun.png -------------------------------------------------------------------------------- /shop/public/images/master.svg: -------------------------------------------------------------------------------- 1 | Mastercard -------------------------------------------------------------------------------- /shop/public/images/mobile-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /shop/public/images/new-shop/box.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /shop/public/images/new-shop/photo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /shop/public/images/new-shop/text.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /shop/public/images/new-window-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /shop/public/images/payment/dai.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /shop/public/images/payment/eth.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /shop/public/images/payment/ogn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /shop/public/images/screenshot-discount.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/shop/public/images/screenshot-discount.png -------------------------------------------------------------------------------- /shop/public/images/screenshot-discount@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/shop/public/images/screenshot-discount@2x.png -------------------------------------------------------------------------------- /shop/public/images/screenshot-discount@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/shop/public/images/screenshot-discount@3x.png -------------------------------------------------------------------------------- /shop/public/images/screenshot-note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/shop/public/images/screenshot-note.png -------------------------------------------------------------------------------- /shop/public/images/screenshot-note@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/shop/public/images/screenshot-note@2x.png -------------------------------------------------------------------------------- /shop/public/images/screenshot-note@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/shop/public/images/screenshot-note@3x.png -------------------------------------------------------------------------------- /shop/public/images/sendgrid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OriginProtocol/dshop/f9f386058455e675ce6dbd47e4ab60311bfdfbe7/shop/public/images/sendgrid.png -------------------------------------------------------------------------------- /shop/public/images/spinner-animation-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /shop/public/images/spinner-blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /shop/public/images/spinner.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /shop/public/images/upload-icon-gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /shop/public/images/upload.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /shop/scripts/getCss.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require('child_process') 2 | const Styl = require('react-styl') 3 | 4 | const css = execSync( 5 | `find ./src -type f -print | xargs awk "/require\\('react-styl'\\)\\(/{flag=1; next} /\\\`\\)/{flag=0} flag"` 6 | ) 7 | Styl(css.toString()) 8 | console.log(Styl.getCss()) 9 | -------------------------------------------------------------------------------- /shop/scripts/splitTranslations.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | 3 | const rawTranslations = fs.readFileSync(`${__dirname}/../.translated_fbts.json`) 4 | const translations = JSON.parse(rawTranslations) 5 | const translationsDir = `${__dirname}/../public/translations` 6 | 7 | fs.mkdirSync(translationsDir, { recursive: true }) 8 | Object.keys(translations).forEach((lang) => { 9 | fs.writeFileSync( 10 | `${translationsDir}/${lang}.json`, 11 | JSON.stringify(translations[lang], null, 2) 12 | ) 13 | }) 14 | -------------------------------------------------------------------------------- /shop/src/components/ConfigLoader.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { useLocation } from 'react-router-dom' 3 | 4 | import useConfig from 'utils/useConfig' 5 | 6 | const ConfigLoader = ({ children }) => { 7 | const location = useLocation() 8 | const { config, setActiveShop } = useConfig() 9 | 10 | useEffect(() => { 11 | if (!config) { 12 | setActiveShop(true) 13 | } 14 | }, [config]) 15 | 16 | useEffect(() => { 17 | if (location.state && location.state.scrollToTop) { 18 | window.scrollTo(0, 0) 19 | } 20 | }, [location.pathname]) 21 | 22 | if (!config) { 23 | return 'Loading' 24 | } 25 | 26 | return children 27 | } 28 | 29 | export default ConfigLoader 30 | -------------------------------------------------------------------------------- /shop/src/components/CurrencySelect.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { useStateValue } from 'data/state' 4 | import { AllCurrencies } from 'data/Currencies' 5 | 6 | const CurrencySelect = ({ className }) => { 7 | const [{ preferredCurrency }, dispatch] = useStateValue() 8 | 9 | const onChange = (e) => { 10 | dispatch({ type: 'setPreferredCurrency', currency: e.target.value }) 11 | } 12 | 13 | return ( 14 | 21 | ) 22 | } 23 | 24 | export default CurrencySelect 25 | -------------------------------------------------------------------------------- /shop/src/components/DshopProvider.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { StateProvider } from 'data/state' 4 | import ConfigLoader from './ConfigLoader' 5 | 6 | const DshopProvider = ({ children }) => { 7 | return ( 8 | 9 | {children} 10 | 11 | ) 12 | } 13 | 14 | export default DshopProvider 15 | -------------------------------------------------------------------------------- /shop/src/components/Link.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link, NavLink } from 'react-router-dom' 3 | 4 | const ScrollToTopLink = ({ useNavLink, scrollToTop = true, ...props }) => { 5 | let { to } = props 6 | if (typeof to === 'string') { 7 | to = { pathname: to, state: { scrollToTop } } 8 | } 9 | if (useNavLink) { 10 | return 11 | } 12 | return 13 | } 14 | 15 | export default ScrollToTopLink 16 | -------------------------------------------------------------------------------- /shop/src/components/Loading.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import fbt from 'fbt' 3 | 4 | const Loading = () => { 5 | const [hidden, setHidden] = useState(true) 6 | useEffect(() => { 7 | const to = setTimeout(() => { 8 | setHidden(false) 9 | }, 100) 10 | return function cleanup() { 11 | clearTimeout(to) 12 | } 13 | }) 14 | return hidden ? null : ( 15 | <> 16 | Loading... 17 | 18 | ) 19 | } 20 | 21 | export default Loading 22 | -------------------------------------------------------------------------------- /shop/src/components/Price.js: -------------------------------------------------------------------------------- 1 | import get from 'lodash/get' 2 | import formatPrice from 'utils/formatPrice' 3 | 4 | import useConfig from 'utils/useConfig' 5 | 6 | const Price = ({ amount, free }) => { 7 | const { config } = useConfig() 8 | const currency = get(config, 'currency', 'USD') 9 | return formatPrice(amount, { currency, free }) 10 | } 11 | 12 | export default Price 13 | -------------------------------------------------------------------------------- /shop/src/components/ProductRedirect.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Redirect } from 'react-router-dom' 3 | 4 | import useProducts from 'utils/useProducts' 5 | 6 | const ProductRedirect = () => { 7 | const { products } = useProducts() 8 | if (products && products.length) { 9 | return 10 | } 11 | 12 | return null 13 | } 14 | 15 | export default ProductRedirect 16 | -------------------------------------------------------------------------------- /shop/src/components/Redirect.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Redirect } from 'react-router-dom' 3 | 4 | const ScrollToTopRedirect = (props) => { 5 | let { to } = props 6 | if (typeof to === 'string') { 7 | to = { pathname: to, state: { scrollToTop: true } } 8 | } 9 | return 10 | } 11 | 12 | export default ScrollToTopRedirect 13 | -------------------------------------------------------------------------------- /shop/src/components/SocialLink.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import Twitter from 'components/icons/Twitter' 4 | import Facebook from 'components/icons/Facebook' 5 | import Instagram from 'components/icons/Instagram' 6 | 7 | const CmpMap = { 8 | twitter: Twitter, 9 | facebook: Facebook, 10 | instagram: Instagram 11 | } 12 | 13 | const SocialLink = ({ href, className, svg = {} }) => { 14 | if (!href) return null 15 | 16 | const key = Object.keys(CmpMap).find((s) => href.match(new RegExp(s, 'i'))) 17 | const Cmp = CmpMap[key] 18 | 19 | if (!Cmp) { 20 | return null 21 | } 22 | 23 | return ( 24 | 25 | 26 | 27 | ) 28 | } 29 | 30 | export default SocialLink 31 | -------------------------------------------------------------------------------- /shop/src/components/VariantOptions.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const VariantOptions = ({ variant, product }) => { 4 | const { options, variants } = product 5 | if (!options || variants.length <= 1) { 6 | return null 7 | } 8 | return ( 9 |
10 | {variant.options.map((opt, idx) => ( 11 | {`${product.options[idx]}: ${opt}`} 12 | ))} 13 |
14 | ) 15 | } 16 | 17 | export default VariantOptions 18 | -------------------------------------------------------------------------------- /shop/src/components/admin/PasswordField.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | 3 | const PasswordField = ({ input, field, prepend }) => { 4 | const [hide, setHide] = useState(true) 5 | return ( 6 |
7 | {!prepend ? null : ( 8 |
9 | {prepend} 10 |
11 | )} 12 | 13 |
14 |
21 |
22 | ) 23 | } 24 | 25 | export default PasswordField 26 | -------------------------------------------------------------------------------- /shop/src/components/icons/Bars.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Cart = () => ( 4 | 9 | 10 | 11 | ) 12 | 13 | export default Cart 14 | -------------------------------------------------------------------------------- /shop/src/components/icons/Camera.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Camera = () => ( 4 | 5 | 10 | 11 | ) 12 | 13 | export default Camera 14 | -------------------------------------------------------------------------------- /shop/src/components/icons/Caret.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Caret = () => ( 4 | 5 | 6 | 7 | ) 8 | 9 | export default Caret 10 | -------------------------------------------------------------------------------- /shop/src/components/icons/CheckCircle.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const CheckCircle = () => ( 4 | 5 | 6 | 7 | 8 | ) 9 | 10 | export default CheckCircle 11 | 12 | require('react-styl')(` 13 | .icon.icon-check-circle 14 | path 15 | stroke: #1990c6 16 | stroke-width: 2 17 | `) 18 | -------------------------------------------------------------------------------- /shop/src/components/icons/Clipboard.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Clipboard = () => ( 4 | 12 | 13 | 14 | 15 | ) 16 | 17 | export default Clipboard 18 | -------------------------------------------------------------------------------- /shop/src/components/icons/Close.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Close = ({ className = '', color = '#000' }) => ( 4 | 5 | 13 | 14 | 15 | 16 | ) 17 | 18 | export default Close 19 | -------------------------------------------------------------------------------- /shop/src/components/icons/Facebook.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Facebook = ({ color, ...props }) => ( 4 | 5 | 9 | 10 | ) 11 | 12 | export default Facebook 13 | -------------------------------------------------------------------------------- /shop/src/components/icons/Medium.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Medium = ({ color, ...props }) => ( 4 | 5 | 9 | 10 | ) 11 | 12 | export default Medium 13 | -------------------------------------------------------------------------------- /shop/src/components/icons/Menu.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Menu = ({ className = '', color = '#000' }) => ( 4 | 5 | 6 | 7 | 8 | 9 | ) 10 | 11 | export default Menu 12 | -------------------------------------------------------------------------------- /shop/src/components/icons/Plus.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Plus = ({ className = '', size = 12 }) => ( 4 | 10 | 11 | 12 | 13 | ) 14 | 15 | export default Plus 16 | 17 | require('react-styl')(` 18 | .icon.icon-plus 19 | line 20 | stroke: #3b80ee 21 | .btn:hover .icon.icon-plus 22 | line 23 | stroke: #fff 24 | `) 25 | -------------------------------------------------------------------------------- /shop/src/components/icons/Search.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Search = () => ( 4 | 5 | 9 | 10 | ) 11 | 12 | export default Search 13 | -------------------------------------------------------------------------------- /shop/src/components/icons/Telegram.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Telegram = () => ( 4 | 5 | 6 | 7 | ) 8 | 9 | export default Telegram 10 | -------------------------------------------------------------------------------- /shop/src/components/icons/Twitter.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Twitter = ({ color, ...props }) => ( 4 | 5 | 9 | 10 | ) 11 | 12 | export default Twitter 13 | -------------------------------------------------------------------------------- /shop/src/components/icons/YouTube.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const YouTube = ({ color, ...props }) => ( 4 | 5 | 9 | 10 | ) 11 | 12 | export default YouTube 13 | -------------------------------------------------------------------------------- /shop/src/css/app.css: -------------------------------------------------------------------------------- 1 | @import '~typeface-lato'; -------------------------------------------------------------------------------- /shop/src/css/app.scss: -------------------------------------------------------------------------------- 1 | @import '~bootstrap/scss/bootstrap'; 2 | -------------------------------------------------------------------------------- /shop/src/data/Languages.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | // ['de_DE', 'Deutsch'], 3 | // ['el_GR', 'ελληνικά'], 4 | ['es_ES', 'Español'], 5 | // ['fil_PH', 'Filipino'], 6 | // ['fr_FR', 'Français'], 7 | // ['hr_HR', 'hrvatski jezik'], 8 | // ['id_ID', 'Indonesian'], 9 | // ['it_IT', 'Italiano'], 10 | // ['ja_JP', '日本語'], 11 | ['ko_KR', '한국어'], 12 | // ['nl_NL', 'Nederlands'], 13 | // ['pt_PT', 'Português'], 14 | // ['ro_RO', 'limba română'], 15 | // ['ru_RU', 'Русский'], 16 | // ['th_TH', 'ไทย'], 17 | // ['tr_TR', 'Türkçe'], 18 | // ['uk_UA', 'Українська'], 19 | // ['vi_VN', 'Tiếng Việt'], 20 | ['zh_CN', '简体中文'], 21 | // ['zh_TW', '繁體中文'], 22 | ['en_US', 'English'] 23 | ] 24 | -------------------------------------------------------------------------------- /shop/src/data/Networks.js: -------------------------------------------------------------------------------- 1 | export const Networks = [ 2 | { 3 | id: 1, 4 | idStr: 'mainnet', 5 | name: 'Mainnet' 6 | }, 7 | { 8 | id: 4, 9 | idStr: 'rinkeby', 10 | name: 'Rinkeby' 11 | }, 12 | { 13 | id: 999, 14 | idStr: 'localhost', 15 | name: 'Localhost' 16 | } 17 | ] 18 | 19 | export const NetworksById = Networks.reduce((m, o) => { 20 | m[o.id] = o 21 | return m 22 | }, {}) 23 | 24 | export const NetworksByIdStr = Networks.reduce((m, o) => { 25 | m[o.idStr] = o 26 | return m 27 | }, {}) 28 | -------------------------------------------------------------------------------- /shop/src/data/OfferStates.js: -------------------------------------------------------------------------------- 1 | export default { 2 | Created: 'OfferCreated', 3 | Accepted: 'OfferAccepted', 4 | Finalized: 'OfferFinalized', 5 | Withdrawn: 'OfferWithdrawn', 6 | Disputed: 'OfferDisputed' 7 | } 8 | -------------------------------------------------------------------------------- /shop/src/data/PaymentStates.js: -------------------------------------------------------------------------------- 1 | export default { 2 | Paid: 'Paid', 3 | Refunded: 'Refunded', 4 | Pending: 'Pending', 5 | Rejected: 'Rejected' 6 | } 7 | -------------------------------------------------------------------------------- /shop/src/data/PaymentTypes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | CreditCard: 'CreditCard', 3 | PayPal: 'PayPal', 4 | Offline: 'Offline', 5 | CryptoCurrency: 'CryptoCurrency', 6 | Uphold: 'Uphold' 7 | } 8 | -------------------------------------------------------------------------------- /shop/src/data/fbTrack.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import get from 'lodash/get' 3 | 4 | function fbTrack(state, action) { 5 | if (typeof fbq !== 'function') { 6 | return 7 | } 8 | 9 | if (action.type === 'addToCart') { 10 | fbq('track', 'AddToCart', { 11 | value: get(action, 'item.price'), 12 | currency: 'USD', 13 | contents: [ 14 | { 15 | id: get(action, 'item.product'), 16 | quantity: get(action, 'item.quantity') 17 | } 18 | ] 19 | }) 20 | } else if (action.type === 'orderComplete') { 21 | fbq('track', 'Purchase', { 22 | value: get(state, 'cart.total'), 23 | currency: 'USD' 24 | }) 25 | } else if (action.type === 'updatePaymentMethod') { 26 | fbq('track', 'AddPaymentInfo') 27 | } 28 | } 29 | 30 | export default fbTrack 31 | -------------------------------------------------------------------------------- /shop/src/data/fetchProduct.js: -------------------------------------------------------------------------------- 1 | import memoize from 'lodash/memoize' 2 | 3 | async function fetchProduct(dataSrc, id) { 4 | const raw = await fetch(`${dataSrc}${id}/data.json`) 5 | if (raw.ok) { 6 | return await raw.json() 7 | } else { 8 | return null 9 | } 10 | } 11 | 12 | export default memoize(fetchProduct, (...args) => args.join('-')) 13 | -------------------------------------------------------------------------------- /shop/src/data/fetchProductStock.js: -------------------------------------------------------------------------------- 1 | async function fetchProductStock(productId, config) { 2 | const { backend, backendAuthToken } = config 3 | return await fetch( 4 | `${backend}/products${productId ? `/${productId}` : ''}/stock`, 5 | { 6 | credentials: 'include', 7 | headers: { 8 | authorization: `bearer ${encodeURIComponent(backendAuthToken)}` 9 | } 10 | } 11 | ).then((res) => res.json()) 12 | } 13 | 14 | export default fetchProductStock 15 | -------------------------------------------------------------------------------- /shop/src/data/formData.js: -------------------------------------------------------------------------------- 1 | export default function formData(state) { 2 | return Object.keys(state) 3 | .filter((k) => k.indexOf('Error') < 0) 4 | .reduce((m, o) => { 5 | m[o] = state[o] 6 | return m 7 | }, {}) 8 | } 9 | -------------------------------------------------------------------------------- /shop/src/pages/OrderLoader.js: -------------------------------------------------------------------------------- 1 | import React, { Suspense } from 'react' 2 | import fbt from 'fbt' 3 | 4 | const Order = React.lazy(() => import('./Order')) 5 | 6 | const OrderLoader = () => ( 7 | 10 | Loading 11 | 12 | } 13 | > 14 | 15 | 16 | ) 17 | 18 | export default OrderLoader 19 | -------------------------------------------------------------------------------- /shop/src/pages/admin/Loader.js: -------------------------------------------------------------------------------- 1 | import React, { Suspense } from 'react' 2 | import fbt from 'fbt' 3 | 4 | const Admin = React.lazy(() => import('./Admin')) 5 | 6 | const AdminLoader = () => ( 7 | 10 | Loading... 11 | 12 | } 13 | > 14 | 15 | 16 | ) 17 | 18 | export default AdminLoader 19 | -------------------------------------------------------------------------------- /shop/src/pages/admin/auth/SignUp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import fbt from 'fbt' 4 | 5 | import SetupLayout from 'pages/super-admin/setup/_SetupLayout' 6 | import SignUp from 'pages/super-admin/setup/SignUp' 7 | import Link from 'components/Link' 8 | 9 | const Signup = () => { 10 | return ( 11 | 12 |
13 | 14 |
15 |
16 | 17 | Already have an account? 18 | 19 | Login} 23 | /> 24 |
25 |
26 | ) 27 | } 28 | 29 | export default Signup 30 | -------------------------------------------------------------------------------- /shop/src/pages/admin/discounts/_Tabs.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { NavLink } from 'react-router-dom' 3 | import fbt from 'fbt' 4 | 5 | const DiscountTabs = () => { 6 | return ( 7 |
    8 |
  • 9 | 10 | Manual 11 | 12 |
  • 13 |
  • 14 | 15 | Automatic 16 | 17 |
  • 18 |
19 | ) 20 | } 21 | 22 | export default DiscountTabs 23 | -------------------------------------------------------------------------------- /shop/src/pages/admin/settings/payments/_CreateListing.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | 3 | import CreateListingTx from './_CreateListingTx' 4 | 5 | const CreateListing = ({ className, children, onCreated }) => { 6 | const [submit, setSubmit] = useState() 7 | return ( 8 | <> 9 |