├── public └── favicon.ico ├── utils ├── req.js ├── error.js └── fetch-helpers.js ├── easydb ├── package.json ├── .gitignore └── dbviewer.js ├── css └── app.css ├── .gitignore ├── server ├── auth.js ├── api-helpers.js ├── db.js └── shopify-server.js ├── pages ├── api │ ├── validate-token.js │ ├── shopify │ │ ├── install-auth-url.js │ │ └── install-granted.js │ └── debug.js ├── app │ ├── index.js │ ├── login.js │ └── shopify-welcome.js ├── debug.js └── index.js ├── ReadMe.md ├── components ├── shop-url.js ├── with │ ├── app-auth.js │ └── app-layout.js └── nav.js ├── next.config.js ├── package.json └── client └── shopify-client.js /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisandrewca/shopify-nextjs/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /utils/req.js: -------------------------------------------------------------------------------- 1 | export const redirect = (res, code, path) => { 2 | res.writeHeader(code, { Location: path }); 3 | res.end(); 4 | } -------------------------------------------------------------------------------- /easydb/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dbviewer", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node dbviewer.js" 7 | }, 8 | "dependencies": { 9 | "easydb-io": "^2.0.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /css/app.css: -------------------------------------------------------------------------------- 1 | .shop-url { 2 | width: 35rem; 3 | margin-bottom: 1rem; 4 | font: 400 1.6rem "ShopifySans", -apple-system, BlinkMacSystemFont, San Francisco, Roboto, Segoe UI, Helvetica Neue, sans-serif; 5 | } 6 | 7 | .shop-url label { 8 | font-size: 1.8rem; 9 | } 10 | 11 | .shop-url .Polaris-TextField__Input { 12 | padding: 1.6rem; 13 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | .env* 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | -------------------------------------------------------------------------------- /easydb/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | .env* 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | -------------------------------------------------------------------------------- /server/auth.js: -------------------------------------------------------------------------------- 1 | import jwt from 'jsonwebtoken' 2 | import { log } from '../utils/error' 3 | 4 | export const generateJwt = async (payload) => { 5 | const token = jwt.sign(payload, process.env.FOR_SERVER_CODE_JWT_SECRET, { expiresIn: 60, algorithm: 'HS256' }); 6 | await log('generated jwt', { payload, token }); 7 | return token; 8 | } 9 | 10 | export const validateJwt = async (token) => { 11 | const valid = jwt.verify(token, process.env.FOR_SERVER_CODE_JWT_SECRET); 12 | await log('validated jwt', { token, valid }); 13 | return valid; 14 | } -------------------------------------------------------------------------------- /pages/api/validate-token.js: -------------------------------------------------------------------------------- 1 | import { parseCookies } from 'nookies' 2 | import { allGood, runApi } from "../../server/api-helpers" 3 | import { tryParse } from '../../utils/error' 4 | import { validateJwt } from '../../server/auth' 5 | 6 | export const GET = async (req, res) => { 7 | const cookies = parseCookies({ req }); 8 | const { token } = tryParse(cookies['user']); 9 | const authorized = await validateJwt(token); 10 | allGood(res, { authorized }); 11 | } 12 | 13 | export default async (req, res) => { 14 | await runApi(req, res, { GET }); 15 | } -------------------------------------------------------------------------------- /pages/api/shopify/install-auth-url.js: -------------------------------------------------------------------------------- 1 | import { getAuthUrl } from "../../../server/shopify-server" 2 | import { allGood, badRequest, runApi } from "../../../server/api-helpers" 3 | 4 | export const GET = async (req, res) => { 5 | const { shopUrl } = req.query; 6 | const authUrl = await getAuthUrl(shopUrl); 7 | if (!authUrl) { 8 | return badRequest(res, 'Invalid shop url'); 9 | } 10 | 11 | allGood(res, { authUrl, apiKey: process.env.FOR_SERVER_CODE_SHOPIFY_API_KEY }); 12 | } 13 | 14 | export default async (req, res) => { 15 | await runApi(req, res, { GET }); 16 | } -------------------------------------------------------------------------------- /pages/api/debug.js: -------------------------------------------------------------------------------- 1 | export default async (req, res) => { 2 | console.info(`BASE_URL: ${process.env.BASE_URL}`); 3 | console.info(`SHOPIFY_REDIRECT_URL: ${process.env.SHOPIFY_REDIRECT_URL}`); 4 | console.info(`FOR_SERVER_CODE_SHOPIFY_API_KEY: ${process.env.FOR_SERVER_CODE_SHOPIFY_API_KEY}`); 5 | console.info(`FOR_SERVER_CODE_SHOPIFY_API_SECRET: ${process.env.FOR_SERVER_CODE_SHOPIFY_API_SECRET}`); 6 | console.info(`typeof window ${typeof window}`); 7 | 8 | res.status(200).json({ 9 | BASE_URL: process.env.BASE_URL, 10 | SHOPIFY_REDIRECT_URL: process.env.SHOPIFY_REDIRECT_URL 11 | }); 12 | } -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | ### Configuration 2 | Create .\\.env with 3 | 4 | BASE_URL= 5 | SHOPIFY_REDIRECT_URL= 6 | FOR_SERVER_CODE_COOKIE_DOMAIN= 7 | FOR_SERVER_CODE_SHOPIFY_API_KEY= 8 | FOR_SERVER_CODE_SHOPIFY_API_SECRET= 9 | FOR_SERVER_CODE_JWT_SECRET= 10 | 11 | Create .\\.env.local with 12 | 13 | SRDBG=TRUE 14 | 15 | Create 1-click, no-login db at [www.easydb.io](https://www.easydb.io). Put API keys into .\\server\\db.js. 16 | 17 | ### Run 18 | 1. Setup your shopify app in the partner dashboard, you'll need a public facing https url 19 | 2. Fill out the configuration 20 | 3. `npm install` 21 | 4. `npm run dev` -------------------------------------------------------------------------------- /easydb/dbviewer.js: -------------------------------------------------------------------------------- 1 | const EasyDB = require('easydb-io/bundle.js')({ 2 | database: '', 3 | token: '' 4 | }) 5 | 6 | const db = EasyDB; 7 | 8 | // Use a callback 9 | // db.put('myKey', {some: 'data'}, err => console.info(err)) 10 | // db.get('myKey', (err, value) => console.info(value, err)) 11 | // db.delete('myKey', err => console.info(err)) 12 | // db.list((err, value) => console.info(value, err)) 13 | 14 | // Or, async/await 15 | (async () => { 16 | let value, values 17 | // value = await db.put('myKey', {some: 'data'}) 18 | // value = await db.get('myKey') 19 | // value = await db.delete('myKey') 20 | values = await db.list() 21 | console.info(values); 22 | })() -------------------------------------------------------------------------------- /components/shop-url.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { Button, Form, TextField } from '@shopify/polaris' 3 | 4 | const ShopUrl = ({ onSubmit }) => { 5 | const [shopUrl, setShopUrl] = useState(''); 6 | 7 | return (
{JSON.stringify(props)}
9 |
23 | via procces.env
22 |BASE_URL: {process.env.BASE_URL}
23 |SHOPIFY_REDIRECT_URL: {process.env.SHOPIFY_REDIRECT_URL}
24 |via getInitialProps
27 |{props.debug}
28 | {debug}
32 |
18 | To get started, edit pages/index.js and save to reload.
19 |