Merge pull request #5598 from supabase/master

Prod deploy to Studio
This commit is contained in:
Joshen Lim authored and GitHub committed 2022-02-22 18:01:00 +08:00
commit 864b3fd97a
85 files changed
+3807 -5947

No files matched your search

+1 -1
View File
@@ -181,6 +181,7 @@ Our approach for client libraries is modular. Each sub-library is a standalone i
- [Arabic | العربية](/i18n/README.ar.md)
- [Albanian / Shqip](/i18n/README.sq.md)
- [Bangla / বাংলা](/i18n/README.bn.md)
- [Catalan / Català](/i18n/README.ca.md)
- [Danish / Dansk](/i18n/README.da.md)
- [Dutch / Nederlands](/i18n/README.nl.md)
@@ -213,7 +214,6 @@ Our approach for client libraries is modular. Each sub-library is a standalone i
- [Turkish / Türkçe](/i18n/README.tr.md)
- [Ukrainian / Українська](/i18n/README.uk.md)
- [Vietnamese / Tiếng Việt](/i18n/README.vi-vn.md)
- [Bangla / বাংলা](/i18n/README.bn.md)
- [List of translations](/i18n/languages.md) <!--- Keep only this -->
---
-5
View File
@@ -1,8 +1,3 @@
node_modules
build
.docusaurus
docs/client
docs/gotrue/client
docs/postgrest/client
docs/realtime/client
docs/reference
-268
View File
@@ -1,268 +0,0 @@
/**
* Usage:
* ts-node ReferenceGenerator.ts -o {output_dir} {input}.yml
*
* Example:
* ts-node ReferenceGenerator.ts -o docs/client spec/supabase.yml
*/
import Example from './spec/gen/components/Example'
import Page from './spec/gen/components/Page'
import Sidebar from './spec/gen/components/Sidebar'
import SidebarCategory from './spec/gen/components/SidebarCategory'
import Tab from './spec/gen/components/Tab'
import Tabs from './spec/gen/components/Tabs'
import { slugify, tsDocCommentToMdComment, writeToDisk } from './spec/gen/lib/helpers'
import { TsDoc, OpenRef } from './spec/gen/definitions'
const yaml = require('js-yaml')
const fs = require('fs')
const main = (fileNames, options) => {
try {
const outputDir = options.o || options.output || ''
fileNames.forEach((inputFileName) => {
gen(inputFileName, outputDir)
})
return
} catch (e) {
console.log(e)
}
}
async function gen(inputFileName, outputDir) {
const docSpec = yaml.safeLoad(fs.readFileSync(inputFileName, 'utf8'))
const defRef = fs.readFileSync(docSpec.info.definition, 'utf8')
if (!defRef) return
const definition = JSON.parse(defRef)
const id = docSpec.info.id
const allLanguages = docSpec.info.libraries
const pages = Object.entries(docSpec.pages).map(([name, x]: [string, OpenRef.Page]) => ({
...x,
pageName: name,
}))
// Sidebar
const inputFileNameToSnakeCase = inputFileName.replace('/', '_').replace('.yml', '')
const sidebarFileName = `sidebar_${inputFileNameToSnakeCase}.js`
const sidebar = generateSidebar(docSpec)
await writeToDisk(sidebarFileName, sidebar)
console.log('Sidebar created: ', sidebarFileName)
// Index Page
const indexFilename = outputDir + `/index.mdx`
const index = generateDocsIndexPage(docSpec, inputFileName)
await writeToDisk(indexFilename, index)
console.log('The index was saved: ', indexFilename)
// Generate Pages
pages.forEach(async (pageSpec: OpenRef.Page) => {
try {
const slug = slugify(pageSpec.pageName)
const hasTsRef = pageSpec['$ref'] || null
const tsDefinition = hasTsRef && extractTsDocNode(hasTsRef, definition)
if (hasTsRef && !tsDefinition) throw new Error('Definition not found: ' + hasTsRef)
const description =
pageSpec.description || tsDocCommentToMdComment(getDescriptionFromDefintion(tsDefinition))
// Create page
const content = Page({
slug,
id: slug,
specFileName: inputFileName,
title: pageSpec.title || pageSpec.pageName,
description,
parameters: hasTsRef ? generateParameters(tsDefinition) : '',
spotlight: generateSpotlight(id, pageSpec['examples'] || [], allLanguages),
examples: generateExamples(id, pageSpec['examples'] || [], allLanguages),
notes: pageSpec.notes,
})
// Write to disk
const dest = outputDir + `/${slug}.mdx`
await writeToDisk(dest, content)
console.log('Saved: ', dest)
} catch (error) {
console.error(error)
}
})
}
function generateParameters(tsDefinition: any) {
let functionDeclaration = null
if (tsDefinition.kindString == 'Method') {
functionDeclaration = tsDefinition
} else if (tsDefinition.kindString == 'Constructor') {
functionDeclaration = tsDefinition
} else functionDeclaration = tsDefinition?.type?.declaration
if (!functionDeclaration) return ''
const paramDefinitions: TsDoc.TypeDefinition[] = functionDeclaration.signatures[0].parameters // PMC: seems flaky.. why the [0]?
if (!paramDefinitions) return ''
// const paramsComments: TsDoc.CommentTag = tsDefinition.comment?.tags?.filter(x => x.tag == 'param')
let parameters = paramDefinitions.map((x) => recurseThroughParams(x)).join(`\n`)
return methodListGroup(parameters)
}
function getDescriptionFromDefintion(tsDefinition) {
if (!tsDefinition) return null
if (['Method', 'Constructor', 'Constructor signature'].includes(tsDefinition.kindString))
return tsDefinition?.signatures[0].comment
else return tsDefinition?.comment || ''
}
function recurseThroughParams(paramDefinition: TsDoc.TypeDefinition) {
let children = paramDefinition.type?.declaration?.children
const labelParams = {
name: paramDefinition.name,
isOptional: !!paramDefinition.flags.isOptional,
type: extractParamTypeAsString(paramDefinition),
description: paramDefinition.comment ? tsDocCommentToMdComment(paramDefinition.comment) : null,
}
let subContent = ''
if (!!children) {
let properties = children
.sort((a, b) => a.name.localeCompare(b.name)) // first alphabetical
.sort((a, b) => (a.flags?.isOptional ? 1 : -1)) // required params first
.map((x) => recurseThroughParams(x))
let heading = `<h5 class="method-list-title method-list-title-isChild expanded">Properties</h5>`
subContent = methodListGroup([heading].concat(properties).join('\n'))
}
return methodListItemLabel(labelParams, subContent)
}
const methodListGroup = (items) => `
<ul className="method-list-group">
${items}
</ul>
`
const methodListItemLabel = ({ name, isOptional, type, description }, subContent) => `
<li className="method-list-item">
<h4 className="method-list-item-label">
<span className="method-list-item-label-name">
${name}
</span>
<span className="method-list-item-label-badge ${!isOptional && 'required'}">
${isOptional ? 'optional' : 'required'}
</span>
<span className="method-list-item-validation">
${type}
</span>
</h4>
<div class="method-list-item-description">
${description ? description : 'No description provided. '}
</div>
${subContent}
</li>
`
function generateExamples(id: string, specExamples: any, allLanguages: any) {
return specExamples.map((example) => {
let allTabs = example.hideCodeBlock
? ''
: Tabs(id, allLanguages, generateTabs(allLanguages, example))
return Example({ name: example.name, description: example.description, tabs: allTabs })
})
}
/**
* A spotlight is an example which appears at the top of the page.
*/
function generateSpotlight(id: string, specExamples: any, allLanguages: any) {
const spotlight = specExamples.find((x) => x.isSpotlight) || null
const spotlightContent = !spotlight
? ''
: Tabs(id, allLanguages, generateTabs(allLanguages, spotlight))
return spotlightContent
}
function generateTabs(allLanguages: any, example: any) {
return allLanguages
.map((library) => {
let content = example[library.id] || notImplemented
return Tab(library.id, content)
})
.join('\n')
}
const notImplemented = `
\`\`\`
Not yet implemented
\`\`\`
`
function extractParamTypeAsString(paramDefinition) {
if (paramDefinition.type?.name) {
return `<code>${paramDefinition.type.name}</code>`
}
if (paramDefinition.type?.type == 'union') {
return paramDefinition.type.types
.map((x) =>
x.value
? `<code>${x.value}</code>`
: x.name
? `<code>${x.name}</code>`
: x.type
? `<code>${x.type}</code>`
: ''
)
.join(' | ')
} else {
return '<code>object</code>'
}
}
/**
* Iterates through the definition to find the correct definition.
* You can pass it a deeply nested node using dot notation. eg: 'LoggedInUser.data.email'
*/
function extractTsDocNode(nodeToFind: string, definition: any) {
const nodePath = nodeToFind.split('.')
let i = 0
let previousNode = definition
let currentNode = definition
while (i < nodePath.length) {
previousNode = currentNode
currentNode = previousNode.children.find((x) => x.name == nodePath[i]) || null
if (currentNode == null) {
console.log(`Cant find ${nodePath[i]} in ${previousNode.children.map((x) => '\n' + x.name)}`)
break
}
i++
}
return currentNode
}
function generateSidebar(docSpec: any) {
let path = docSpec.info.docs.path || ''
let categories = docSpec.info.docs.sidebar.map((x) => {
const items = x.items.map((item) => {
let slug = slugify(item)
return `'${path}${slug}'`
})
return SidebarCategory(x.name, items)
})
return Sidebar(categories)
}
function generateDocsIndexPage(docSpec: any, inputFileName: string) {
return Page({
slug: slugify(docSpec.info.title),
id: 'index',
title: docSpec.info.title,
specFileName: inputFileName,
description: docSpec.info.description,
})
}
// Run everything
const argv = require('minimist')(process.argv.slice(2))
main(argv['_'], argv)
+11 -11
View File
@@ -4,28 +4,28 @@ title: Work at Supabase
description: We're hiring. Start your career at Supabase.
---
import Roles from '@site/docs/careers/snippets/roles.mdx'
import About from '@site/docs/careers/snippets/about.mdx'
import Team from '@site/docs/careers/snippets/team.mdx'
import Apply from '@site/docs/careers/snippets/apply.mdx'
import Process from '@site/docs/careers/snippets/process.mdx'
import Roles from '@site/docs/careers/_snippets/roles.mdx'
import About from '@site/docs/careers/_snippets/about.mdx'
import Team from '@site/docs/careers/_snippets/team.mdx'
import Apply from '@site/docs/careers/_snippets/apply.mdx'
import Process from '@site/docs/careers/_snippets/process.mdx'
## Roles
## Roles
<Roles/>
<Roles />
## About Supabase
<About/>
<About />
## About the team
<Team/>
<Team />
## Apply
<Apply/>
<Apply />
## Process
<Process/>
<Process />
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+5 -5
View File
@@ -5,11 +5,11 @@ title: Auth Engineers.
description: Supabase is hiring Auth Engineers
---
import Roles from '@site/docs/careers/snippets/roles.mdx'
import About from '@site/docs/careers/snippets/about.mdx'
import Team from '@site/docs/careers/snippets/team.mdx'
import Apply from '@site/docs/careers/snippets/apply.mdx'
import Process from '@site/docs/careers/snippets/process.mdx'
import Roles from '@site/docs/careers/_snippets/roles.mdx'
import About from '@site/docs/careers/_snippets/about.mdx'
import Team from '@site/docs/careers/_snippets/team.mdx'
import Apply from '@site/docs/careers/_snippets/apply.mdx'
import Process from '@site/docs/careers/_snippets/process.mdx'
## About the role
+9 -9
View File
@@ -5,20 +5,20 @@ title: Data Engineers.
description: Supabase is hiring Data Engineers
---
import Roles from '@site/docs/careers/snippets/roles.mdx'
import About from '@site/docs/careers/snippets/about.mdx'
import Team from '@site/docs/careers/snippets/team.mdx'
import Apply from '@site/docs/careers/snippets/apply.mdx'
import Process from '@site/docs/careers/snippets/process.mdx'
import Roles from '@site/docs/careers/_snippets/roles.mdx'
import About from '@site/docs/careers/_snippets/about.mdx'
import Team from '@site/docs/careers/_snippets/team.mdx'
import Apply from '@site/docs/careers/_snippets/apply.mdx'
import Process from '@site/docs/careers/_snippets/process.mdx'
## About the role
- Be the business intelligence star player at Supabase by helping dig deep into the data we are generating. Deep knowledge of SQL and relevant tooling is a must.
- Help customer success & sales team by working to identify and monitor:
- Usage Insights
- Find and segment cohorts based around account level data, behavior, traffic sources, and measure their subsequent behaviors
- Find and monitor product qualified leads based around account and behavior characteristics
- Customer health segmentation into actionable cohorts (e.g. healthy, churn, at risk)
- Usage Insights
- Find and segment cohorts based around account level data, behavior, traffic sources, and measure their subsequent behaviors
- Find and monitor product qualified leads based around account and behavior characteristics
- Customer health segmentation into actionable cohorts (e.g. healthy, churn, at risk)
- Help identify opportunities high touch, low touch, & tech touch customer success & sales approaches
- Help sales & customer success identify opportunity for unscheduled outreach
- Working with internal tooling team to identify and implement solutions for growth, sales, customer success teams needs
+5 -6
View File
@@ -5,12 +5,11 @@ title: Developer Advocate (US Timezone).
description: Supabase is hiring Developer Advocates and Community Managers
---
import Roles from '@site/docs/careers/snippets/roles.mdx'
import About from '@site/docs/careers/snippets/about.mdx'
import Team from '@site/docs/careers/snippets/team.mdx'
import Apply from '@site/docs/careers/snippets/apply.mdx'
import Process from '@site/docs/careers/snippets/process.mdx'
import Roles from '@site/docs/careers/_snippets/roles.mdx'
import About from '@site/docs/careers/_snippets/about.mdx'
import Team from '@site/docs/careers/_snippets/team.mdx'
import Apply from '@site/docs/careers/_snippets/apply.mdx'
import Process from '@site/docs/careers/_snippets/process.mdx'
## About the role
+6 -9
View File
@@ -5,12 +5,11 @@ title: Developer Success Engineer
description: Supabase is hiring Developer Success Engineers
---
import Roles from '@site/docs/careers/snippets/roles.mdx'
import About from '@site/docs/careers/snippets/about.mdx'
import Team from '@site/docs/careers/snippets/team.mdx'
import Apply from '@site/docs/careers/snippets/apply.mdx'
import Process from '@site/docs/careers/snippets/process.mdx'
import Roles from '@site/docs/careers/_snippets/roles.mdx'
import About from '@site/docs/careers/_snippets/about.mdx'
import Team from '@site/docs/careers/_snippets/team.mdx'
import Apply from '@site/docs/careers/_snippets/apply.mdx'
import Process from '@site/docs/careers/_snippets/process.mdx'
## About the role
@@ -26,20 +25,18 @@ Supabase is hiring Developer Success Engineers. Help us build the easiest databa
- Triage/clean/close issues on any of our repos.
- Work with the community on all of our channels: GitHub, Discord, and Email.
## About you
## About you
- 2+ years experience in engineering or a highly technical support role.
- You must love remote work.
- You possess exceptional communication skills.
- Availability to work within a weekend and on-call rota.
## Bonus if you:
- Are a Polyglot developer, with some skill in NodeJS/Javascript.
- Have PostgreSQL experience.
## About Supabase
<About />{' '}
+5 -6
View File
@@ -5,12 +5,11 @@ title: Ex-Founders.
description: Supabase is hiring ex-Founders
---
import Roles from '@site/docs/careers/snippets/roles.mdx'
import About from '@site/docs/careers/snippets/about.mdx'
import Team from '@site/docs/careers/snippets/team.mdx'
import Apply from '@site/docs/careers/snippets/apply.mdx'
import Process from '@site/docs/careers/snippets/process.mdx'
import Roles from '@site/docs/careers/_snippets/roles.mdx'
import About from '@site/docs/careers/_snippets/about.mdx'
import Team from '@site/docs/careers/_snippets/team.mdx'
import Apply from '@site/docs/careers/_snippets/apply.mdx'
import Process from '@site/docs/careers/_snippets/process.mdx'
## About the role
+5 -5
View File
@@ -5,11 +5,11 @@ title: Front End Developers
description: Supabase is hiring Front End Developers
---
import Roles from '@site/docs/careers/snippets/roles.mdx'
import About from '@site/docs/careers/snippets/about.mdx'
import Team from '@site/docs/careers/snippets/team.mdx'
import Apply from '@site/docs/careers/snippets/apply.mdx'
import Process from '@site/docs/careers/snippets/process.mdx'
import Roles from '@site/docs/careers/_snippets/roles.mdx'
import About from '@site/docs/careers/_snippets/about.mdx'
import Team from '@site/docs/careers/_snippets/team.mdx'
import Apply from '@site/docs/careers/_snippets/apply.mdx'
import Process from '@site/docs/careers/_snippets/process.mdx'
## About the role
+5 -6
View File
@@ -5,12 +5,11 @@ title: Full Stack Developers.
description: Supabase is hiring Full Stack Developers
---
import Roles from '@site/docs/careers/snippets/roles.mdx'
import About from '@site/docs/careers/snippets/about.mdx'
import Team from '@site/docs/careers/snippets/team.mdx'
import Apply from '@site/docs/careers/snippets/apply.mdx'
import Process from '@site/docs/careers/snippets/process.mdx'
import Roles from '@site/docs/careers/_snippets/roles.mdx'
import About from '@site/docs/careers/_snippets/about.mdx'
import Team from '@site/docs/careers/_snippets/team.mdx'
import Apply from '@site/docs/careers/_snippets/apply.mdx'
import Process from '@site/docs/careers/_snippets/process.mdx'
## About the role
+5 -5
View File
@@ -5,11 +5,11 @@ title: Graphic / Web Designers
description: Supabase is hiring Designers
---
import Roles from '@site/docs/careers/snippets/roles.mdx'
import About from '@site/docs/careers/snippets/about.mdx'
import Team from '@site/docs/careers/snippets/team.mdx'
import Apply from '@site/docs/careers/snippets/apply.mdx'
import Process from '@site/docs/careers/snippets/process.mdx'
import Roles from '@site/docs/careers/_snippets/roles.mdx'
import About from '@site/docs/careers/_snippets/about.mdx'
import Team from '@site/docs/careers/_snippets/team.mdx'
import Apply from '@site/docs/careers/_snippets/apply.mdx'
import Process from '@site/docs/careers/_snippets/process.mdx'
## About the role
+6 -6
View File
@@ -5,11 +5,11 @@ title: Growth Marketer.
description: Supabase is hiring a Growth Marketer
---
import Roles from '@site/docs/careers/snippets/roles.mdx'
import About from '@site/docs/careers/snippets/about.mdx'
import Team from '@site/docs/careers/snippets/team.mdx'
import Apply from '@site/docs/careers/snippets/apply.mdx'
import Process from '@site/docs/careers/snippets/process.mdx'
import Roles from '@site/docs/careers/_snippets/roles.mdx'
import About from '@site/docs/careers/_snippets/about.mdx'
import Team from '@site/docs/careers/_snippets/team.mdx'
import Apply from '@site/docs/careers/_snippets/apply.mdx'
import Process from '@site/docs/careers/_snippets/process.mdx'
## About the role
@@ -35,7 +35,7 @@ The ideal candidate has experience helping grow products which enable developers
- Help running email campaigns
- Community management
## Skills and Experience
## Skills and Experience
- Building marketing funnels and running outbound campaigns
- Creating and managing social media campaigns
+5 -6
View File
@@ -5,12 +5,11 @@ title: Postgres Experts.
description: Supabase is hiring Postgres Experts
---
import Roles from '@site/docs/careers/snippets/roles.mdx'
import About from '@site/docs/careers/snippets/about.mdx'
import Team from '@site/docs/careers/snippets/team.mdx'
import Apply from '@site/docs/careers/snippets/apply.mdx'
import Process from '@site/docs/careers/snippets/process.mdx'
import Roles from '@site/docs/careers/_snippets/roles.mdx'
import About from '@site/docs/careers/_snippets/about.mdx'
import Team from '@site/docs/careers/_snippets/team.mdx'
import Apply from '@site/docs/careers/_snippets/apply.mdx'
import Process from '@site/docs/careers/_snippets/process.mdx'
## About the role
+5 -6
View File
@@ -5,12 +5,11 @@ title: Cloud Platform Engineers and SREs.
description: Supabase is hiring Cloud Platform Engineers and SREs
---
import Roles from '@site/docs/careers/snippets/roles.mdx'
import About from '@site/docs/careers/snippets/about.mdx'
import Team from '@site/docs/careers/snippets/team.mdx'
import Apply from '@site/docs/careers/snippets/apply.mdx'
import Process from '@site/docs/careers/snippets/process.mdx'
import Roles from '@site/docs/careers/_snippets/roles.mdx'
import About from '@site/docs/careers/_snippets/about.mdx'
import Team from '@site/docs/careers/_snippets/team.mdx'
import Apply from '@site/docs/careers/_snippets/apply.mdx'
import Process from '@site/docs/careers/_snippets/process.mdx'
## About the role
+6 -7
View File
@@ -5,11 +5,11 @@ title: Support Engineers.
description: Supabase is hiring Support Engineers
---
import Roles from '@site/docs/careers/snippets/roles.mdx'
import About from '@site/docs/careers/snippets/about.mdx'
import Team from '@site/docs/careers/snippets/team.mdx'
import Apply from '@site/docs/careers/snippets/apply.mdx'
import Process from '@site/docs/careers/snippets/process.mdx'
import Roles from '@site/docs/careers/_snippets/roles.mdx'
import About from '@site/docs/careers/_snippets/about.mdx'
import Team from '@site/docs/careers/_snippets/team.mdx'
import Apply from '@site/docs/careers/_snippets/apply.mdx'
import Process from '@site/docs/careers/_snippets/process.mdx'
## About the role
@@ -35,7 +35,6 @@ For the right candidate, additional responsibilities may include:
- Familiar with popular JavaScript frameworks (React, Vue, Svelte) and Node.js.
- Experience working with GitHub source code management, and CI/CD tools.
## Nice to have
- Experience writing tests
@@ -47,7 +46,7 @@ For the right candidate, additional responsibilities may include:
## Four day work week
This role is optionally a four-day workweek if you are interested in working from Friday to Monday. We can also discuss a shorter (pro-rated) week.
This role is optionally a four-day workweek if you are interested in working from Friday to Monday. We can also discuss a shorter (pro-rated) week.
In particular we would like to hire people who would be willing to work on weekends (or at least one of the days (Saturday or Sunday).
## About Supabase
+5 -6
View File
@@ -5,11 +5,11 @@ title: Technical Recruiter
description: Supabase is hiring Technical Recruiters
---
import Roles from '@site/docs/careers/snippets/roles.mdx'
import About from '@site/docs/careers/snippets/about.mdx'
import Team from '@site/docs/careers/snippets/team.mdx'
import Apply from '@site/docs/careers/snippets/apply.mdx'
import Process from '@site/docs/careers/snippets/process.mdx'
import Roles from '@site/docs/careers/_snippets/roles.mdx'
import About from '@site/docs/careers/_snippets/about.mdx'
import Team from '@site/docs/careers/_snippets/team.mdx'
import Apply from '@site/docs/careers/_snippets/apply.mdx'
import Process from '@site/docs/careers/_snippets/process.mdx'
## About the role
@@ -38,7 +38,6 @@ import Process from '@site/docs/careers/snippets/process.mdx'
- You're Comfortable coordinating all candidate touch points
- You can plan out batch days & other projects to drive excellence for our candidate experience and quality of interviews
## About Supabase
<About />{' '}
+9 -9
View File
@@ -5,11 +5,11 @@ title: Technical Writers.
description: Supabase is hiring Technical Writers
---
import Roles from '@site/docs/careers/snippets/roles.mdx'
import About from '@site/docs/careers/snippets/about.mdx'
import Team from '@site/docs/careers/snippets/team.mdx'
import Apply from '@site/docs/careers/snippets/apply.mdx'
import Process from '@site/docs/careers/snippets/process.mdx'
import Roles from '@site/docs/careers/_snippets/roles.mdx'
import About from '@site/docs/careers/_snippets/about.mdx'
import Team from '@site/docs/careers/_snippets/team.mdx'
import Apply from '@site/docs/careers/_snippets/apply.mdx'
import Process from '@site/docs/careers/_snippets/process.mdx'
## About the role
@@ -19,16 +19,16 @@ Do you have experience building for the web?
Are you an engaging writer with an eye for detail?
Responsibilities includes:
Responsibilities includes:
- Blog posts for the Supabase website
- Helping other team members communicate their wins with edits and suggestions
- Writing internal docs such as: documenting processes, how-to guides etc.
- Writing and Organizing our documentation, and generally setting the direction on how they are structured
- Writing and Organizing our documentation, and generally setting the direction on how they are structured
- Helping with enterprise orientated marketing collateral: thought leadership, white papers, case studies
- Working directly with the Founders, Growth, and Dev Rel teams on strategy
Must Have:
Must Have:
- Intermediate understanding of Postgres
- Full stack development experience
@@ -38,7 +38,7 @@ Must Have:
Nice to Have:
- Understanding of SEO
- Understanding of SEO
- Previous experience with SaaS content marketing
## About Supabase
-7
View File
@@ -1,9 +1,3 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
module.exports = {
title: 'Supabase',
tagline: 'Build in a weekend. Scale to millions.',
@@ -169,7 +163,6 @@ module.exports = {
},
],
],
scripts: [{ src: '/scripts/telemetry.js' }],
plugins: [
// [
// '@docusaurus/plugin-content-docs',
+3 -3
View File
@@ -13308,9 +13308,9 @@
"dev": true
},
"ua-parser-js": {
"version": "0.7.22",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.22.tgz",
"integrity": "sha512-YUxzMjJ5T71w6a8WWVcMGM6YWOTX27rCoIQgLXiWaxqXSx9D7DNjiGWn1aJIRSQ5qr0xuhra77bSIh6voR/46Q=="
"version": "0.7.31",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz",
"integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ=="
},
"unherit": {
"version": "1.1.3",
+1 -12
View File
@@ -1,11 +1,3 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
module.exports = {
docs: [
{
@@ -18,10 +10,7 @@ module.exports = {
type: 'category',
label: 'See Also',
collapsed: false,
items: [
'handbook/contributing',
'handbook/supasquad',
],
items: ['handbook/contributing', 'handbook/supasquad'],
},
],
}
-71
View File
@@ -1,71 +0,0 @@
require('dotenv').config()
var axios = require('axios')
var fs = require('fs')
const query = `
query BIO_QUERY($username: String!) {
organization(login: $username) {
id
sponsorshipsAsMaintainer(first: 100) {
totalCount
nodes {
createdAt
privacyLevel
tier {
name
}
sponsor {
login
}
}
}
}
}
`
const fetchAllSponsors = async () => {
const { data } = await axios.post(
`https://api.github.com/graphql`,
{
query,
variables: {
username: 'supabase',
},
},
{
headers: {
'Content-Type': 'application/json',
Authorization: `token ${process.env.GITHUB_SPONSORS_TOKEN}`,
},
}
)
return data.data.organization.sponsorshipsAsMaintainer
}
const formatResults = (graphqlResponse) => {
return graphqlResponse.nodes
.filter((x) => !!x.sponsor)
.map((x) => ({
tier: x.tier.name,
sponsor: x.sponsor.login,
}))
}
const writeFile = (data) => {
fs.writeFile(
'./src/data/sponsors.json',
JSON.stringify(data, null, 2),
'utf8',
function (err) {
if (err) throw err
console.log('sponsors.json saved.')
}
)
}
const main = async () => {
const sponsorsResponse = await fetchAllSponsors()
const formatted = formatResults(sponsorsResponse)
writeFile(formatted)
}
main()
-10
View File
@@ -1,10 +0,0 @@
import React from 'react'
export default function Collapsable({ title, children }) {
return (
<details className="Collapsable">
<summary>{title}</summary>
<div>{children}</div>
</details>
)
}
-66
View File
@@ -1,66 +0,0 @@
import React, { useState } from 'react'
import CodeBlock from '@theme/CodeBlock'
export default function CustomCodeBlock({
header,
code,
highlight,
js,
jsHighlight,
response,
language,
}) {
const [showResponse, toggleResponse] = useState(false)
let wrapperClass = ''
if (header) wrapperClass += ' code-with-header'
if (response) wrapperClass += ' code-with-response'
return (
<>
<div className={wrapperClass}>
{header && <div className="code-header">{header}</div>}
<>
{code && (
<CodeBlock className={language || 'bash'} metastring={highlight}>
{code}
</CodeBlock>
)}
{js && <CodeBlock metastring={jsHighlight}>{js}</CodeBlock>}
</>
</div>
{response && (
<>
<div className={'code-with-header repsonse-header'}>
<a
className="code-header has-hover-pointer"
style={showResponse ? styles.responseShown : styles.responseHidden}
onClick={() => toggleResponse(!showResponse)}
>
{showResponse ? 'Hide' : 'Show'} Response
</a>
{showResponse && <CodeBlock>{response}</CodeBlock>}
</div>
</>
)}
</>
)
}
const styles = {
responseShown: {
textAlign: 'right',
display: 'block',
borderRadius: '0',
color: 'var(--custom-primary)',
borderTop: '1px solid #444',
},
responseHidden: {
textAlign: 'right',
display: 'block',
borderBottom: 'none',
borderRadius: '0 0 4px 4px',
borderTop: '1px solid #444',
color: '#ccc',
},
}
-67
View File
@@ -1,67 +0,0 @@
import React, { useState } from 'react'
import extensions from '../data/extensions.json'
export default function Extensions() {
const [filter, setFilter] = useState('')
return (
<>
<div style={styles.inputContainer}>
<input
type="text"
style={styles.input}
placeholder="Filter"
onChange={(e) => setFilter(e.target.value)}
/>
</div>
<div className="row" style={styles.row}>
{extensions
.filter((x) => x.name.indexOf(filter) >= 0)
.map((extension) => (
<div className={'col col--6'} key={extension.name} style={styles.column}>
<div style={styles.card}>
<h3>
<code style={styles.title}>{extension.name}</code>
</h3>
<p style={styles.description}>
{extension.comment.charAt(0).toUpperCase() + extension.comment.slice(1)}
</p>
</div>
</div>
))}
</div>
</>
)
}
const styles = {
inputContainer: {
marginBottom: '15px',
},
input: {
border: '1px solid var(--ifm-panel-border-color)',
borderRadius: 4,
backgroundColor: 'var(--custom-background-color-diff)',
color: 'var(--ifm-font-color-base)',
margin: 0,
padding: '6px 8px',
width: 300,
},
column: {
margin: '5px 0px',
padding: '0px 5px',
},
card: {
border: '1px solid var(--ifm-panel-border-color)',
borderRadius: 5,
padding: 8,
},
title: {
margin: 0,
fontSize: '0.9rem',
border: '1px solid var(--ifm-panel-border-color)',
},
description: {
fontSize: '0.8rem',
margin: 0,
},
}
-12
View File
@@ -1,12 +0,0 @@
import React from 'react'
export default function Figure({ src, alt, caption }) {
return (
<div className="Figure">
<figure>
<img src={src} alt={alt} />
<figcaption>{caption}</figcaption>
</figure>
</div>
)
}
-36
View File
@@ -1,36 +0,0 @@
import React from 'react'
export default function GithubCard({ title, description, href, stars, handle }) {
return (
<a className={'card'} href={href} style={{ height: '100%' }}>
<div className="card__body">
<h4 style={styles.h4} style={{ margin: 0 }}>
{title.toUpperCase()}
</h4>
<small>{description}</small>
</div>
<hr style={styles.hr} />
<div style={styles.cardBase}>
<div>@{handle}</div>
<div>{stars} ★</div>
</div>
</a>
)
}
const styles = {
hr: {
margin: '15px 0 10px 0',
},
h3: {
margin: 0,
textTransform: 'capitalize',
},
cardBase: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
padding: '0 15px 10px 15px',
fontSize: '0.8em',
},
}
-52
View File
@@ -1,52 +0,0 @@
import React from 'react'
export default function HowCard({ title, description, featureTitle, features }) {
return (
<div className={'HowCard'}>
<div className={'card'} style={styles.card}>
<h4 style={styles.h4}>{title}</h4>
<div className="card__body" style={styles.cardBase}>
{description}
<h5 style={styles.h5}>{featureTitle}</h5>
<div style={styles.features}>
{features.map((x, i) => (
<div key={i}>
<span>
<img src="/img/check.svg" alt="✔" style={styles.check} />
</span>
<span>{x}</span>
</div>
))}
</div>
</div>
</div>
</div>
)
}
const styles = {
h4: {
padding: 10,
textAlign: 'center',
borderBottom: '1px solid #000',
margin: 0,
},
h5: {
textTransform: 'uppercase',
// opacity: 0.5,
margin: '10px 0 0px 0',
fontSize: '0.9em',
},
card: {
border: '1px solid #000',
boxShadow: 'none',
},
cardBase: {
fontSize: '0.9em',
},
check: {
width: 10,
height: 10,
marginRight: 10,
},
}
-36
View File
@@ -1,36 +0,0 @@
import React, { useEffect, useState } from 'react'
import mermaid from 'mermaid'
const theme = `
.node rect { fill: rgba(62,207,142, 0.3); stroke: #24b47e; }
.cluster rect { fill: var(--custom-background-color-highlight); stroke: #999; }
.edgePath path { fill: var(--custom-primary) }
.edgePath .path { stroke: var(--custom-primary) }
g.classGroup rect { fill: rgba(62,207,142, 0.3); stroke: #24b47e; }
g.classGroup line { stroke: var(--custom-primary); }
g.classGroup text { fill: var(--ifm-color-content); color: var(--ifm-color-content); }
.classLabel .label { fill: var(--ifm-color-content); color: var(--ifm-color-content); }
classGroup .title { fill: var(--ifm-color-content); color: var(--ifm-color-content); }
composition { stroke: var(--custom-primary); }
aggregation { stroke: var(--custom-primary); }
dependency { stroke: var(--custom-primary); }
.relation { stroke: var(--custom-primary); }
`
mermaid.initialize({ themeCSS: theme })
export default function Mermaid({ graph, classNames, caption }) {
let [rendered, setRendered] = useState(<div></div>)
useEffect(() => {
mermaid.render('mermaid-ID', graph.toString(), (html) => setRendered(html))
}, [graph])
return (
<div className={`Mermaid ${classNames || ''}`}>
{caption && <figcaption>{caption}</figcaption>}
<div className="graph" dangerouslySetInnerHTML={{ __html: rendered }}></div>
</div>
)
}
-71
View File
@@ -1,71 +0,0 @@
import React, { useRef, useEffect } from 'react'
import PropTypes from 'prop-types'
export default function Section({ title, id, style, children }) {
const hash = String(id ? id : title)
.trim()
.replace(/\s/g, '-')
.toLowerCase()
const sectionRef = useRef(null)
useEffect(() => {
function scrollOnHash() {
const clearedLocationHash = window.location.hash.substring(1)
if (clearedLocationHash == hash) {
sectionRef.current.scrollIntoView({ behavior: 'smooth' })
}
}
function changeHash() {
const el = document.getElementById(hash)
if (el) {
const elementPos = el.getBoundingClientRect()
if (elementPos.top <= 80 && elementPos.top >= 0) replaceHistoryHash()
}
}
scrollOnHash()
window.addEventListener('scroll', changeHash)
return () => {
window.removeEventListener('scroll', changeHash)
}
}, [])
function replaceHistoryHash() {
history.replaceState(history.state, '', `#${hash}`)
}
function handleHashClick() {
replaceHistoryHash()
sectionRef.current.scrollIntoView({ behavior: 'smooth' })
}
return (
<section className="section-lg">
<div className="container Section">
<h2
className={`${stylePropOptions[style]} Section-Title`}
ref={sectionRef}
onClick={handleHashClick}
id={hash}
>
{title}
<span className="Section-Hash">#</span>
</h2>
<div>{children}</div>
</div>
</section>
)
}
const stylePropOptions = {
underline: 'with-underline',
}
Section.propTypes = {
title: PropTypes.string.isRequired,
style: PropTypes.oneOf(['underline']),
}
+6 -293
View File
@@ -33,9 +33,9 @@
/*
Customization here for both Light and Dark themes
*/
--custom-font-base: 'Inter', BlinkMacSystemFont, -apple-system, 'Segoe UI', 'Roboto',
'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 'Helvetica',
'Arial', sans-serif;
--custom-font-base: 'Inter', BlinkMacSystemFont, -apple-system, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 'Helvetica', 'Arial',
sans-serif;
--custom-font-code: 'office code pro', Menlo, monospace;
--custom-font-size-base: 1rem;
--custom-border-radius: 3px;
@@ -236,7 +236,9 @@ body {
display: inline-block;
}
h4, h5, h6 {
h4,
h5,
h6 {
font-weight: bold !important;
}
@@ -652,20 +654,6 @@ div[class^='sidebar_'] .menu__link.menu__link--active:not(.menu__link--sublist)
}
}
.DummyData {
overflow-x: auto;
}
.DummyData div {
padding-right: 30px;
}
.Collapsable summary {
font-size: 0.9rem;
margin-bottom: 20px;
}
.Collapsable summary:hover {
cursor: pointer;
}
div[class^='codeBlockContent'],
div[class*=' codeBlockContent'],
.prism-code {
@@ -804,49 +792,6 @@ a.hash-link {
display: none;
}
.HowSections .col {
margin-bottom: 20px;
}
.HowSections img.diagram {
margin: 0px auto 30px auto;
display: block;
}
.HowCard {
position: relative;
}
.HowCard::before,
.HowCard::after {
bottom: 100%;
border: solid transparent;
content: '';
height: 0;
left: 50%;
width: 0;
position: absolute;
pointer-events: none;
margin-bottom: -1px;
}
.HowCard::before {
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 5px solid black;
border-width: 12px;
margin-left: -12px;
}
.HowCard::after {
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 5px solid var(--ifm-card-background-color);
border-width: 11px;
margin-left: -11px;
}
.Blog .code-with-header {
margin-bottom: 30px;
}
@@ -854,46 +799,6 @@ a.hash-link {
color: var(--ifm-color-content);
}
.Figure {
text-align: center;
margin: 30px 0;
}
.Figure figure {
margin: 0 auto;
width: 100%;
}
.Figure img {
box-shadow: 0px 0px 5px 5px rgba(0, 0, 0, 0.2);
border-radius: 4px;
}
.Figure figcaption {
font-size: 0.8rem;
}
.Mermaid {
text-align: center;
margin: 80px 0;
font-family: var(--custom-font-base);
color: var(--ifm-color-content);
font-size: 0.9rem;
}
.Mermaid .graph {
overflow-x: auto;
margin: 0px calc(var(--ifm-spacing-horizontal) * -1);
padding: var(--ifm-spacing-horizontal);
}
.Mermaid div,
.Mermaid tspan,
.Mermaid g.classGroup text {
font-family: var(--custom-font-base);
color: var(--ifm-color-content);
/* font-size: 0.9em; */
}
.Mermaid figcaption {
font-size: 0.8rem;
margin-bottom: 20px;
}
.DocSearch-Button {
border-radius: 8px !important;
}
@@ -1075,198 +980,6 @@ html[data-theme='dark'] .navbar-item-twitter:before {
margin-top: 5px;
}
.w-full {
width: 100%;
max-width: 100%;
}
/* Section */
.Section {
position: relative;
display: flex;
flex-direction: column;
}
.Section-Hash {
margin-left: 8px;
color: var(--custom-content-color-light);
opacity: 0.4;
}
.Section-Title:hover .Section-Hash {
color: var(--custom-primary);
opacity: 1;
}
.Section-Title {
cursor: pointer;
scroll-margin: 74px; /* Navbar size (60) + a little margin */
}
/* Showcase */
.ShowcaseCard {
position: relative;
height: 100%;
display: flex;
flex-direction: column;
box-shadow: none;
border: 1px solid #333;
padding: var(--ifm-card-vertical-spacing) var(--ifm-card-horizontal-spacing);
}
.ShowcaseCard:hover {
transition: all 0.2s ease;
border: 1px solid var(--custom-primary);
box-shadow: var(--custom-shadow-xl), 0 0 0 1px var(--custom-primary) !important;
}
.ShowcaseCard-Img {
height: 100%;
width: auto;
}
.ShowcaseCard-ImgContainer {
height: 140px;
display: flex;
justify-content: center;
}
.ShowcaseCard-Text {
height: 100%;
display: flex;
flex-direction: column;
}
.ShowcaseCard-Text p {
width: 100%;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
margin: 0;
}
.ShowcaseCard-Text h3 {
opacity: 0;
position: absolute;
width: 100%;
top: 0;
left: 0;
backdrop-filter: blur(3px) contrast(80%);
padding: var(--ifm-card-vertical-spacing) var(--ifm-card-horizontal-spacing);
transition: 0.2s ease;
}
.ShowcaseCard-Content {
display: flex;
flex-direction: column;
margin-bottom: var(--ifm-card-vertical-spacing);
height: 100%;
}
.ShowcaseCard:hover h3 {
opacity: 1;
}
.ShowcaseCard-Tag {
color: var(--custom-primary);
font-weight: bold;
font-size: 12px;
margin: 8px 4px 12px 0px;
}
.ShowcaseCard-Footer {
display: flex;
flex-direction: row;
justify-content: flex-start;
}
.ShowcaseCard-Footer-Item {
width: 100%;
}
/* Spaces between children */
.ShowcaseCard-Footer-Spaced .ShowcaseCard-Footer-Item:first-child {
margin-right: 4px;
}
.ShowcaseCard-Footer-Spaced .ShowcaseCard-Footer-Item:nth-child(even) {
margin-left: 4px;
margin-right: 4px;
}
.ShowcaseCard-Footer-Spaced .ShowcaseCard-Footer-Item:last-child {
margin-right: 0px;
}
@media screen and (max-width: 997px) {
.ShowcaseCard-ImgContainer {
height: 120px;
align-self: center;
}
.ShowcaseCard-Content {
display: flex;
flex-direction: row;
}
.ShowcaseCard-Text {
width: 100%;
margin-left: var(--ifm-card-horizontal-spacing);
}
.ShowcaseCard-Tag {
margin-top: 0px;
}
.ShowcaseCard-Footer a {
margin: var(--ifm-card-vertical-spacing) var(--ifm-card-horizontal-spacing) 0 0;
}
.ShowcaseCard-Text h3 {
position: relative;
bottom: 0;
opacity: 1;
backdrop-filter: blur(0) contrast(100%);
padding: 0;
}
}
@media screen and (max-width: 650px) {
.ShowcaseCard-ImgContainer {
height: 80px;
}
}
@media screen and (max-width: 650px) {
.ShowcaseCard-Content {
flex-direction: column;
padding: 0px;
}
.ShowcaseCard-Text {
margin: 0px;
padding: 0px;
}
.ShowcaseCard-Text h3 {
margin-top: var(--ifm-card-vertical-spacing);
}
}
@media screen and (max-width: 380px) {
.ShowcaseCard-Footer {
flex-direction: column;
}
/* Spaces between children */
.ShowcaseCard-Footer-Spaced .ShowcaseCard-Footer-Item:first-child {
margin-right: 0px;
}
.ShowcaseCard-Footer-Spaced .ShowcaseCard-Footer-Item:nth-child(even) {
margin-left: 0px;
margin-right: 0px;
}
}
/* New CSS */
code {
-7
View File
@@ -1,10 +1,3 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* CSS files with the .module.css suffix will be treated as CSS modules
* and scoped locally.
+57 -57
View File
@@ -39,74 +39,74 @@
}
},
"@next/env": {
"version": "12.0.10",
"resolved": "https://registry.npmjs.org/@next/env/-/env-12.0.10.tgz",
"integrity": "sha512-mQVj0K6wQ5WEk/sL9SZ+mJXJUaG7el8CpZ6io1uFe9GgNTSC7EgUyNGqM6IQovIFc5ukF4O/hqsdh3S/DCgT2g=="
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/env/-/env-12.1.0.tgz",
"integrity": "sha512-nrIgY6t17FQ9xxwH3jj0a6EOiQ/WDHUos35Hghtr+SWN/ntHIQ7UpuvSi0vaLzZVHQWaDupKI+liO5vANcDeTQ=="
},
"@next/swc-android-arm64": {
"version": "12.0.10",
"resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.0.10.tgz",
"integrity": "sha512-xYwXGkNhzZZsM5MD7KRwF5ZNiC8OLPtVMUiagpPnwENg8Hb0GSQo/NbYWXM8YrawEwp9LaZ7OXiuRKPh2JyBdA==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.1.0.tgz",
"integrity": "sha512-/280MLdZe0W03stA69iL+v6I+J1ascrQ6FrXBlXGCsGzrfMaGr7fskMa0T5AhQIVQD4nA/46QQWxG//DYuFBcA==",
"optional": true
},
"@next/swc-darwin-arm64": {
"version": "12.0.10",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.0.10.tgz",
"integrity": "sha512-f2zngulkpIJKWHckhRi7X8GZ+J/tNgFF7lYIh7Qx15JH0OTBsjkqxORlkzy+VZyHJ5sWTCaI6HYYd3ow6qkEEg==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.0.tgz",
"integrity": "sha512-R8vcXE2/iONJ1Unf5Ptqjk6LRW3bggH+8drNkkzH4FLEQkHtELhvcmJwkXcuipyQCsIakldAXhRbZmm3YN1vXg==",
"optional": true
},
"@next/swc-darwin-x64": {
"version": "12.0.10",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.0.10.tgz",
"integrity": "sha512-Qykcu/gVC5oTvOQoRBhyuS5GYm5SbcgrFTsaLFkGBmEkg9eMQRiaCswk4IafpDXVzITkVFurzSM28q3tLW2qUw==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.0.tgz",
"integrity": "sha512-ieAz0/J0PhmbZBB8+EA/JGdhRHBogF8BWaeqR7hwveb6SYEIJaDNQy0I+ZN8gF8hLj63bEDxJAs/cEhdnTq+ug==",
"optional": true
},
"@next/swc-linux-arm-gnueabihf": {
"version": "12.0.10",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.0.10.tgz",
"integrity": "sha512-EhqrTFsIXAXN9B/fiiW/QKUK/lSLCXRsLalkUp58KDfMqVLLlj1ORbESAcswiNQOChLuHQSldGEEtOBPQZcd9A==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.0.tgz",
"integrity": "sha512-njUd9hpl6o6A5d08dC0cKAgXKCzm5fFtgGe6i0eko8IAdtAPbtHxtpre3VeSxdZvuGFh+hb0REySQP9T1ttkog==",
"optional": true
},
"@next/swc-linux-arm64-gnu": {
"version": "12.0.10",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.0.10.tgz",
"integrity": "sha512-kqGtC72g3+JYXZbY2ca6digXR5U6AQ6Dzv4eAxYluMePLHjI/Xye1mf9dwVsgmeXfrD/IRDp5K/3A6UNvBm4oQ==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.0.tgz",
"integrity": "sha512-OqangJLkRxVxMhDtcb7Qn1xjzFA3s50EIxY7mljbSCLybU+sByPaWAHY4px97ieOlr2y4S0xdPKkQ3BCAwyo6Q==",
"optional": true
},
"@next/swc-linux-arm64-musl": {
"version": "12.0.10",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.0.10.tgz",
"integrity": "sha512-bG9zTSNwnSgc1Un/7oz1ZVN4UeXsTWrsQhAGWU78lLLCn4Zj9HQoUCRCGLt0OVs2DBZ+WC8CzzFliQ1SKipVbg==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.0.tgz",
"integrity": "sha512-hB8cLSt4GdmOpcwRe2UzI5UWn6HHO/vLkr5OTuNvCJ5xGDwpPXelVkYW/0+C3g5axbDW2Tym4S+MQCkkH9QfWA==",
"optional": true
},
"@next/swc-linux-x64-gnu": {
"version": "12.0.10",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.0.10.tgz",
"integrity": "sha512-c79PcfWtyThiYRa1+3KVfDq0zXaI8o1d6dQWNVqDrtLz5HKM/rbjLdvoNuxDwUeZhxI/d9CtyH6GbuKPw5l/5A==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.0.tgz",
"integrity": "sha512-OKO4R/digvrVuweSw/uBM4nSdyzsBV5EwkUeeG4KVpkIZEe64ZwRpnFB65bC6hGwxIBnTv5NMSnJ+0K/WmG78A==",
"optional": true
},
"@next/swc-linux-x64-musl": {
"version": "12.0.10",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.0.10.tgz",
"integrity": "sha512-g/scgn+21/MLfizOCZOZt+MxNj2/8Tdlwjvy+QZcSUPZRUI2Y5o3HwBvI1f/bSci+NGRU+bUAO0NFtRJ9MzH5w==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.0.tgz",
"integrity": "sha512-JohhgAHZvOD3rQY7tlp7NlmvtvYHBYgY0x5ZCecUT6eCCcl9lv6iV3nfu82ErkxNk1H893fqH0FUpznZ/H3pSw==",
"optional": true
},
"@next/swc-win32-arm64-msvc": {
"version": "12.0.10",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.0.10.tgz",
"integrity": "sha512-gl6B/ravwMeY5Nv4Il2/ARYJQ6u+KPRwGMjS1ZrNudIKlNn4YBeXh5A4cIVm+dHaff6/O/lGOa5/SUYDMZpkww==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.0.tgz",
"integrity": "sha512-T/3gIE6QEfKIJ4dmJk75v9hhNiYZhQYAoYm4iVo1TgcsuaKLFa+zMPh4056AHiG6n9tn2UQ1CFE8EoybEsqsSw==",
"optional": true
},
"@next/swc-win32-ia32-msvc": {
"version": "12.0.10",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.0.10.tgz",
"integrity": "sha512-7RVpZ3tSThC6j+iZB0CUYmFiA3kXmN+pE7QcfyAxFaflKlaZoWNMKHIEZDuxSJc6YmQ6kyxsjqxVay2F5+/YCg==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.0.tgz",
"integrity": "sha512-iwnKgHJdqhIW19H9PRPM9j55V6RdcOo6rX+5imx832BCWzkDbyomWnlzBfr6ByUYfhohb8QuH4hSGEikpPqI0Q==",
"optional": true
},
"@next/swc-win32-x64-msvc": {
"version": "12.0.10",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.0.10.tgz",
"integrity": "sha512-oUIWRKd24jFLRWUYO1CZmML5+32BcpVfqhimGaaZIXcOkfQW+iqiAzdqsv688zaGtyKGeB9ZtiK3NDf+Q0v+Vw==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.0.tgz",
"integrity": "sha512-aBvcbMwuanDH4EMrL2TthNJy+4nP59Bimn8egqv6GHMVj0a44cU6Au4PjOhLNqEh9l+IpRGBqMTzec94UdC5xg==",
"optional": true
},
"@supabase/gotrue-js": {
@@ -658,27 +658,27 @@
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"nanoid": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz",
"integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA=="
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
"integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw=="
},
"next": {
"version": "12.0.10",
"resolved": "https://registry.npmjs.org/next/-/next-12.0.10.tgz",
"integrity": "sha512-1y3PpGzpb/EZzz1jgne+JfZXKAVJUjYXwxzrADf/LWN+8yi9o79vMLXpW3mevvCHkEF2sBnIdjzNn16TJrINUw==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/next/-/next-12.1.0.tgz",
"integrity": "sha512-s885kWvnIlxsUFHq9UGyIyLiuD0G3BUC/xrH0CEnH5lHEWkwQcHOORgbDF0hbrW9vr/7am4ETfX4A7M6DjrE7Q==",
"requires": {
"@next/env": "12.0.10",
"@next/swc-android-arm64": "12.0.10",
"@next/swc-darwin-arm64": "12.0.10",
"@next/swc-darwin-x64": "12.0.10",
"@next/swc-linux-arm-gnueabihf": "12.0.10",
"@next/swc-linux-arm64-gnu": "12.0.10",
"@next/swc-linux-arm64-musl": "12.0.10",
"@next/swc-linux-x64-gnu": "12.0.10",
"@next/swc-linux-x64-musl": "12.0.10",
"@next/swc-win32-arm64-msvc": "12.0.10",
"@next/swc-win32-ia32-msvc": "12.0.10",
"@next/swc-win32-x64-msvc": "12.0.10",
"@next/env": "12.1.0",
"@next/swc-android-arm64": "12.1.0",
"@next/swc-darwin-arm64": "12.1.0",
"@next/swc-darwin-x64": "12.1.0",
"@next/swc-linux-arm-gnueabihf": "12.1.0",
"@next/swc-linux-arm64-gnu": "12.1.0",
"@next/swc-linux-arm64-musl": "12.1.0",
"@next/swc-linux-x64-gnu": "12.1.0",
"@next/swc-linux-x64-musl": "12.1.0",
"@next/swc-win32-arm64-msvc": "12.1.0",
"@next/swc-win32-ia32-msvc": "12.1.0",
"@next/swc-win32-x64-msvc": "12.1.0",
"caniuse-lite": "^1.0.30001283",
"postcss": "8.4.5",
"styled-jsx": "5.0.0",
@@ -686,9 +686,9 @@
},
"dependencies": {
"caniuse-lite": {
"version": "1.0.30001305",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001305.tgz",
"integrity": "sha512-p7d9YQMji8haf0f+5rbcv9WlQ+N5jMPfRAnUmZRlNxsNeBO3Yr7RYG6M2uTY1h9tCVdlkJg6YNNc4kiAiBLdWA=="
"version": "1.0.30001312",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz",
"integrity": "sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ=="
}
}
},
File diff suppressed because it is too large. Load diff
@@ -7,9 +7,8 @@
"start": "next start"
},
"dependencies": {
"@supabase/supabase-js": "^1.11.9",
"@supabase/ui": "^0.24.2",
"next": "11.1.1",
"@supabase/supabase-auth-helpers": "^1.0.6",
"next": "^11.1.4",
"react": "17.0.2",
"react-dom": "17.0.2",
"swr": "^0.5.5"
@@ -1,13 +1,13 @@
import { Auth } from '@supabase/ui'
import { supabase } from '../utils/initSupabase'
import { UserProvider } from '@supabase/supabase-auth-helpers/react'
import { supabaseClient } from '@supabase/supabase-auth-helpers/nextjs'
import './../style.css'
export default function MyApp({ Component, pageProps }) {
return (
<main className={'dark'}>
<Auth.UserContextProvider supabaseClient={supabase}>
<UserProvider supabaseClient={supabaseClient}>
<Component {...pageProps} />
</Auth.UserContextProvider>
</UserProvider>
</main>
)
}
@@ -1,8 +0,0 @@
/**
* NOTE: this file is only needed if you're doing SSR (getServerSideProps)!
*/
import { supabase } from '../../utils/initSupabase'
export default function handler(req, res) {
supabase.auth.api.setAuthCookie(req, res)
}
@@ -0,0 +1,3 @@
import { handleAuth } from '@supabase/supabase-auth-helpers/nextjs'
export default handleAuth()
@@ -1,13 +0,0 @@
import { supabase } from '../../utils/initSupabase'
// Example of how to verify and get user data server-side.
const getUser = async (req, res) => {
const token = req.headers.token
const { data: user, error } = await supabase.auth.api.getUser(token)
if (error) return res.status(401).json({ error: error.message })
return res.status(200).json(user)
}
export default getUser
@@ -1,106 +1,43 @@
import Link from 'next/link'
import useSWR from 'swr'
import { Auth, Card, Typography, Space, Button, Icon } from '@supabase/ui'
import { supabase } from '../utils/initSupabase'
import { useUser, Auth } from '@supabase/supabase-auth-helpers/react'
import { supabaseClient } from '@supabase/supabase-auth-helpers/nextjs'
import { useEffect, useState } from 'react'
const fetcher = (url, token) =>
fetch(url, {
method: 'GET',
headers: new Headers({ 'Content-Type': 'application/json', token }),
credentials: 'same-origin',
}).then((res) => res.json())
const Index = () => {
const { user, session } = Auth.useUser()
const { data, error } = useSWR(session ? ['/api/getUser', session.access_token] : null, fetcher)
const [authView, setAuthView] = useState('sign_in')
const LoginPage = () => {
const { user, error } = useUser()
const [data, setData] = useState({})
useEffect(() => {
const { data: authListener } = supabase.auth.onAuthStateChange((event, session) => {
if (event === 'PASSWORD_RECOVERY') setAuthView('forgotten_password')
if (event === 'USER_UPDATED') setTimeout(() => setAuthView('sign_in'), 1000)
// Send session to /api/auth route to set the auth cookie.
// NOTE: this is only needed if you're doing SSR (getServerSideProps)!
fetch('/api/auth', {
method: 'POST',
headers: new Headers({ 'Content-Type': 'application/json' }),
credentials: 'same-origin',
body: JSON.stringify({ event, session }),
}).then((res) => res.json())
})
return () => {
authListener.unsubscribe()
async function loadData() {
const { data } = await supabaseClient.from('test').select('*')
setData(data)
}
}, [])
const View = () => {
if (!user)
return (
<Space direction="vertical" size={8}>
<div>
<img src="https://app.supabase.io/img/supabase-dark.svg" width="96" />
<Typography.Title level={3}>Welcome to Supabase Auth</Typography.Title>
</div>
<Auth
supabaseClient={supabase}
providers={['google', 'github']}
view={authView}
socialLayout="horizontal"
socialButtonSize="xlarge"
/>
</Space>
)
// Only run query once user is logged in.
if (user) loadData()
}, [user])
if (!user)
return (
<Space direction="vertical" size={6}>
{authView === 'forgotten_password' && <Auth.UpdatePassword supabaseClient={supabase} />}
{user && (
<>
<Typography.Text>You're signed in</Typography.Text>
<Typography.Text strong>Email: {user.email}</Typography.Text>
<Button
icon={<Icon type="LogOut" />}
type="outline"
onClick={() => supabase.auth.signOut()}
>
Log out
</Button>
{error && <Typography.Text type="danger">Failed to fetch user!</Typography.Text>}
{data && !error ? (
<>
<Typography.Text type="success">
User data retrieved server-side (in API route):
</Typography.Text>
<Typography.Text>
<pre>{JSON.stringify(data, null, 2)}</pre>
</Typography.Text>
</>
) : (
<div>Loading...</div>
)}
<Typography.Text>
<Link href="/profile">
<a>SSR example with getServerSideProps</a>
</Link>
</Typography.Text>
</>
)}
</Space>
<>
{error && <p>{error.message}</p>}
<Auth
// view="update_password"
supabaseClient={supabaseClient}
providers={['google', 'github']}
socialLayout="horizontal"
socialButtonSize="xlarge"
/>
</>
)
}
return (
<div style={{ maxWidth: '420px', margin: '96px auto' }}>
<Card>
<View />
</Card>
</div>
<>
<button onClick={() => supabaseClient.auth.signOut()}>Sign out</button>
<p>user:</p>
<pre>{JSON.stringify(user, null, 2)}</pre>
<p>client-side data fetching with RLS</p>
<pre>{JSON.stringify(data, null, 2)}</pre>
</>
)
}
export default Index
export default LoginPage
@@ -1,41 +1,13 @@
import Link from 'next/link'
import { Card, Typography, Space } from '@supabase/ui'
import { supabase } from '../utils/initSupabase'
// pages/profile.js
import { withAuthRequired } from '@supabase/supabase-auth-helpers/nextjs'
export default function Profile({ user }) {
return (
<div style={{ maxWidth: '420px', margin: '96px auto' }}>
<Card>
<Space direction="vertical" size={6}>
<Typography.Text>You're signed in</Typography.Text>
<Typography.Text strong>Email: {user.email}</Typography.Text>
<Typography.Text type="success">
User data retrieved server-side (from Cookie in getServerSideProps):
</Typography.Text>
<Typography.Text>
<pre>{JSON.stringify(user, null, 2)}</pre>
</Typography.Text>
<Typography.Text>
<Link href="/">
<a>Static example with useSWR</a>
</Link>
</Typography.Text>
</Space>
</Card>
</div>
<>
<div>Hello {user.email}</div>
<pre>{JSON.stringify(user, null, 2)}</pre>
</>
)
}
export async function getServerSideProps({ req }) {
const { user } = await supabase.auth.api.getUserByCookie(req)
if (!user) {
// If no user, redirect to index.
return { props: {}, redirect: { destination: '/', permanent: false } }
}
// If there is a user, return it.
return { props: { user } }
}
export const getServerSideProps = withAuthRequired()
@@ -1,6 +0,0 @@
import { createClient } from '@supabase/supabase-js'
export const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
)
File diff suppressed because it is too large. Load diff
+5 -14
View File
@@ -1317,6 +1317,8 @@
},
"@supabase/supabase-js": {
"version": "0.35.10",
"resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-0.35.10.tgz",
"integrity": "sha512-mjEvM5cHL0zu7iBZepl0OyNBOXmNmKa7POkutSvME6rLWW+Fu4j+IJ6rFjqc8I884yRggMoxkEc506YnMSp+jg==",
"requires": {
"@supabase/doctest-js": "^0.1.0",
"@supabase/postgrest-js": "^0.17.0",
@@ -3057,17 +3059,6 @@
"indent-string": "^4.0.0"
}
},
"ajv": {
"version": "6.12.3",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
"integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==",
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"amdefine": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
@@ -10361,9 +10352,9 @@
}
},
"ajv": {
"version": "6.12.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
"integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==",
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
File diff suppressed because it is too large. Load diff
+3 -3
View File
@@ -17009,9 +17009,9 @@
}
},
"url-parse": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz",
"integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==",
"version": "1.5.7",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz",
"integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==",
"requires": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
+4 -4
View File
@@ -1625,7 +1625,7 @@
"@svgr/plugin-svgo" "^5.4.0"
loader-utils "^2.0.0"
"@tailwindcss/postcss7-compat@^2.0.2", "tailwindcss@npm:@tailwindcss/postcss7-compat":
"@tailwindcss/postcss7-compat@^2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@tailwindcss/postcss7-compat/-/postcss7-compat-2.0.2.tgz#49cb21703dfb4447620fceab5cef3285cff8c69d"
integrity sha512-KM8kjG5dd8qoXBX2a6r3r1TOqhFh8NtFBheG9qpVPwSjrD8wRdoM7s+Xz56HEA1XmeN64gEKqjmY6vm55DiS3Q==
@@ -10982,9 +10982,9 @@ url-loader@4.1.1:
schema-utils "^3.0.0"
url-parse@^1.4.3:
version "1.4.7"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
version "1.5.7"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.7.tgz#00780f60dbdae90181f51ed85fb24109422c932a"
integrity sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==
dependencies:
querystringify "^2.1.1"
requires-port "^1.0.0"
+6 -6
View File
@@ -8609,9 +8609,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==",
"version": "1.14.8",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
"funding": [
{
"type": "individual",
@@ -28333,9 +28333,9 @@
}
},
"follow-redirects": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg=="
"version": "1.14.8",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA=="
},
"for-in": {
"version": "1.0.2",
+1 -1
View File
@@ -2,6 +2,7 @@
- [Arabic | العربية](/i18n/README.ar.md)
- [Albanian / Shqip](/i18n/README.sq.md)
- [Bangla / বাংলা](/i18n/README.bn.md)
- [Catalan / Català](/i18n/README.ca.md)
- [Danish / Dansk](/i18n/README.da.md)
- [Dutch / Nederlands](/i18n/README.nl.md)
@@ -36,4 +37,3 @@
- [Turkish / Türkçe](/i18n/README.tr.md)
- [Ukrainian / Українська](/i18n/README.uk.md)
- [Vietnamese / Tiếng Việt](/i18n/README.vi-vn.md)
- [Bangla / বাংলা](/i18n/README.bn.md)
@@ -16,11 +16,10 @@ import {
import { Dictionary } from '@supabase/grid'
import { makeAutoObservable } from 'mobx'
import { POSTGRES_DATA_TYPES } from 'lib/constants'
import { useStore } from 'hooks'
import Panel from 'components/to-be-cleaned/Panel'
import SqlEditor from 'components/to-be-cleaned/SqlEditor'
import { POSTGRES_DATA_TYPES } from 'components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.constants'
class CreateFunctionFormState {
id: number | undefined
@@ -780,12 +779,16 @@ const SelectLanguage: FC = observer(({}) => {
<Select.Option value="sql">sql</Select.Option>
{
//map through all selected extensions that start with pl
enabledExtensions.filter((ex:any) => {
return ex.name.startsWith('pl')
}).map(ex => (<Select.Option key={ex.name} value={ex.name}>{ex.name}</Select.Option>))
enabledExtensions
.filter((ex: any) => {
return ex.name.startsWith('pl')
})
.map((ex) => (
<Select.Option key={ex.name} value={ex.name}>
{ex.name}
</Select.Option>
))
}
</Select>
</div>
)
@@ -33,6 +33,8 @@ const InvoicesSettings: FC<Props> = ({ organization }) => {
useEffect(() => {
let cancel = false
const page = 1
const fetchInvoiceCount = async () => {
const res = await head(`${API_URL}/stripe/invoices?customer=${stripe_customer_id}`, [
'X-Total-Count',
@@ -45,6 +47,9 @@ const InvoicesSettings: FC<Props> = ({ organization }) => {
}
}
}
setPage(page)
fetchInvoices(page)
fetchInvoiceCount()
return () => {
@@ -52,29 +57,23 @@ const InvoicesSettings: FC<Props> = ({ organization }) => {
}
}, [stripe_customer_id])
useEffect(() => {
let cancel = false
const fetchInvoices = async (page: number) => {
setLoading(true)
setPage(page)
const fetchInvoices = async () => {
setLoading(true)
const invoices = await get(
`${API_URL}/stripe/invoices?offset=${offset}&limit=${PAGE_LIMIT}&customer=${stripe_customer_id}`
)
if (!cancel) {
if (invoices.error) {
ui.setNotification({ category: 'error', message: invoices.error.message })
} else {
setInvoices(invoices)
}
setLoading(false)
}
}
fetchInvoices()
const offset = (page - 1) * PAGE_LIMIT
const invoices = await get(
`${API_URL}/stripe/invoices?offset=${offset}&limit=${PAGE_LIMIT}&customer=${stripe_customer_id}`
)
return () => {
cancel = true
if (invoices.error) {
ui.setNotification({ category: 'error', message: invoices.error.message })
} else {
setInvoices(invoices)
}
}, [stripe_customer_id, page])
setLoading(false)
}
return (
<div className="my-4 container max-w-4xl space-y-1">
@@ -140,14 +139,14 @@ const InvoicesSettings: FC<Props> = ({ organization }) => {
type="secondary"
size="tiny"
disabled={page === 1}
onClick={() => setPage(page - 1)}
onClick={async () => await fetchInvoices(page - 1)}
/>
<Button
icon={<IconChevronRight />}
type="secondary"
size="tiny"
disabled={page * PAGE_LIMIT >= count}
onClick={() => setPage(page + 1)}
onClick={async () => await fetchInvoices(page + 1)}
/>
</div>
</div>
@@ -16,16 +16,24 @@ export const TEMPLATES: LogTemplate[] = [
{
label: '10 Minutes Ago',
mode: 'simple',
mode: 'custom',
searchString: 'timestamp < TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 10 MINUTE)',
for: ['database', 'api'],
},
{
label: 'POST or PATCH',
label: 'Commits By User',
mode: 'custom',
searchString:
"REGEXP_CONTAINS(event_message, 'POST') OR REGEXP_CONTAINS(event_message, 'PATCH')",
for: ['api'],
searchString: `SELECT
p.user_name, count(*) as count
FROM postgres_logs
LEFT JOIN UNNEST(metadata) as m ON TRUE
LEFT JOIN UNNEST(m.parsed) AS p ON TRUE
WHERE
REGEXP_CONTAINS(event_message, 'COMMIT')
GROUP BY
p.user_name
`,
for: ['database'],
},
{
label: 'Metadata IP',
@@ -37,7 +45,7 @@ FROM edge_logs
LEFT JOIN UNNEST(r.headers) AS h ON TRUE
WHERE h.x_real_ip IS NOT NULL
`,
for: ['database'],
for: ['api'],
},
{
label: 'Requests by Country',
@@ -51,7 +59,7 @@ FROM edge_logs
GROUP BY
cf.country
ORDER BY
DESC count
count DESC
`,
for: ['api'],
},
@@ -2,7 +2,7 @@ import { sortBy, concat } from 'lodash'
import { PostgresDataTypeOption } from './SidePanelEditor.types'
export const DATE_FORMAT = 'YYYY-MM-DDTHH:mm:ssZ'
export const NUMERICAL_TYPES = ['int2', 'int4', 'int8', 'float4', 'float8']
export const NUMERICAL_TYPES = ['int2', 'int4', 'int8', 'float4', 'float8', 'numeric']
export const JSON_TYPES = ['json', 'jsonb']
export const TEXT_TYPES = ['text', 'varchar']
export const TIMESTAMP_TYPES = ['date', 'time', 'timestamp', 'timetz', 'timestamptz']
@@ -37,6 +37,11 @@ export const POSTGRES_DATA_TYPE_OPTIONS: PostgresDataTypeOption[] = [
description: 'Double precision floating-point number (8 bytes)',
type: 'number',
},
{
name: 'numeric',
description: 'Exact numeric of selectable precision',
type: 'number',
},
{
name: 'json',
description: 'Textual JSON data',
@@ -2,8 +2,10 @@ import useSWR from 'swr'
import { FC } from 'react'
import { useRouter } from 'next/router'
import { get } from 'lib/common/fetch'
import { JwtSecretUpdateStatus, ProjectEvents } from '@supabase/shared-types/out/events'
import { IconAlertCircle, Input, Loading, Typography } from '@supabase/ui'
import { useJwtSecretUpdateStatus } from 'hooks'
import { API_URL } from 'lib/constants'
import Panel from './Panel'
@@ -15,12 +17,20 @@ export const DisplayApiSettings = () => {
const { ref } = router.query
const { data, error }: any = useSWR(`${API_URL}/props/project/${ref}/settings`, get)
const {
isError: isJwtSecretUpdateStatusError,
isLoading: isJwtSecretUpdateStatusLoading,
jwtSecretUpdateStatus,
}: any = useJwtSecretUpdateStatus(ref)
if (!data)
const isNotUpdatingJwtSecret =
jwtSecretUpdateStatus === undefined || jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updated
if (!data || isJwtSecretUpdateStatusLoading)
return (
<ApiContentWrapper>
<Panel.Content className="py-8">
{error ? (
{error || isJwtSecretUpdateStatusError ? (
<div className="flex items-center space-x-2">
<Typography.Text type="secondary">
<IconAlertCircle strokeWidth={2} />
@@ -43,7 +53,7 @@ export const DisplayApiSettings = () => {
return (
<ApiContentWrapper>
{!data ? (
{!data || isJwtSecretUpdateStatusLoading ? (
// @ts-ignore
<Loading active={true} />
) : (
@@ -74,11 +84,17 @@ export const DisplayApiSettings = () => {
</>
}
readOnly
copy
copy={isNotUpdatingJwtSecret}
className="input-mono"
disabled
reveal={x.tags !== 'anon' && true}
value={x.api_key}
reveal={x.tags !== 'anon' && isNotUpdatingJwtSecret}
value={
jwtSecretUpdateStatus === JwtSecretUpdateStatus.Failed
? 'JWT secret update failed, new API key may have issues'
: jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updating
? 'Updating JWT secret...'
: x.api_key
}
onChange={() => {}}
descriptionText={
x.tags === 'service_role'
@@ -98,12 +114,20 @@ export const DisplayConfigSettings = () => {
const { ref } = router.query
const { data, error }: any = useSWR(`${API_URL}/props/project/${ref}/settings`, get)
const {
isError: isJwtSecretUpdateStatusError,
isLoading: isJwtSecretUpdateStatusLoading,
jwtSecretUpdateStatus,
}: any = useJwtSecretUpdateStatus(ref)
if (!data)
const isNotUpdatingJwtSecret =
jwtSecretUpdateStatus === undefined || jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updated
if (!data || isJwtSecretUpdateStatusLoading)
return (
<ConfigContentWrapper>
<Panel.Content className="py-8">
{error ? (
{error || isJwtSecretUpdateStatusError ? (
<div className="flex items-center space-x-2">
<Typography.Text type="secondary">
<IconAlertCircle strokeWidth={2} />
@@ -152,10 +176,16 @@ export const DisplayConfigSettings = () => {
<Input
label="JWT Secret"
readOnly
copy
reveal
copy={isNotUpdatingJwtSecret}
reveal={isNotUpdatingJwtSecret}
disabled
value={jwtSecret}
value={
jwtSecretUpdateStatus === JwtSecretUpdateStatus.Failed
? 'JWT secret update failed'
: jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updating
? 'Updating JWT secret...'
: jwtSecret
}
className="input-mono"
descriptionText="Used to decode your JWTs. You can also use this to mint your own JWTs."
layout="horizontal"
+1
View File
@@ -1,3 +1,4 @@
export * from './useJwtSecretUpdateStatus'
export * from './useOrganizationDetail'
export * from './useProfile'
export * from './useProjectAuthConfig'
@@ -0,0 +1,23 @@
import useSWR from 'swr'
import { get } from 'lib/common/fetch'
import { JwtSecretUpdateStatus } from '@supabase/shared-types/out/events'
import { API_URL } from 'lib/constants'
export function useJwtSecretUpdateStatus(projectRef: string | string[] | undefined) {
const url = `${API_URL}/props/project/${projectRef}/jwt-secret-update-status`
const { data, error, mutate } = useSWR<any>(url, get, {
refreshInterval: (data) =>
data?.jwtSecretUpdateStatus?.meta?.status === JwtSecretUpdateStatus.Updating ? 1_000 : 0,
})
const anyError = data?.error || error
const meta = data?.jwtSecretUpdateStatus?.meta
return {
changeTrackingId: meta?.change_tracking_id,
isError: !!anyError,
isLoading: !anyError && !data,
jwtSecretUpdateError: meta?.error,
jwtSecretUpdateProgress: meta?.progress,
jwtSecretUpdateStatus: meta?.status,
mutateJwtSecretUpdateStatus: mutate,
}
}
-11
View File
@@ -11,17 +11,6 @@ export const PG_META_URL = IS_PLATFORM
export const DATE_FORMAT = 'YYYY-MM-DDTHH:mm:ssZ'
// Data types primarily for mapping icon in editor
export const NUMERICAL_TYPES = ['int2', 'int4', 'int8', 'float4', 'float8']
export const JSON_TYPES = ['json', 'jsonb']
export const TEXT_TYPES = ['text', 'varchar']
export const TIMESTAMP_TYPES = ['date', 'time', 'timestamp', 'timetz', 'timestamptz']
export const OTHER_DATA_TYPES = ['uuid', 'bool']
export const POSTGRES_DATA_TYPES = sortBy(
concat(NUMERICAL_TYPES, JSON_TYPES, TEXT_TYPES, TIMESTAMP_TYPES, OTHER_DATA_TYPES)
)
// Keyboard Shortcuts Related
export const SHORTCUT_KEYS = {
VIEW_ALL_SHORTCUTS: 'VIEW_ALL_SHORTCUTS',
+35 -42
View File
@@ -15,11 +15,12 @@
"@radix-ui/react-tooltip": "^0.1.6",
"@sentry/browser": "^6.15.0",
"@sentry/nextjs": "^6.15.0",
"@supabase/grid": "^2.4.1",
"@supabase/grid": "^2.4.3",
"@supabase/postgres-meta": "^0.26.1",
"@supabase/react-data-grid": "^7.1.0-beta.7",
"@supabase/shared-types": "^0.1.15",
"@supabase/supabase-js": "^1.29.1",
"@supabase/ui": "^0.35.0",
"@supabase/ui": "^0.36.4",
"ajv": "^8.6.3",
"analytics-node": "^3.5.0",
"clipboard": "^2.0.8",
@@ -53,7 +54,7 @@
"sass": "^1.43.4",
"semver": "^6.3.0",
"sqlstring": "^2.3.2",
"swr": "^1.0.1",
"swr": "^1.2.1",
"uniforms-bootstrap4": "^3.6.2",
"uniforms-bridge-json-schema": "^3.6.2",
"zxcvbn": "^4.4.2"
@@ -2679,9 +2680,9 @@
}
},
"node_modules/@supabase/grid": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@supabase/grid/-/grid-2.4.1.tgz",
"integrity": "sha512-KDlm8wSwGqX/UvGMOnKwMSJjq3vlKScSyNNfLnr1UMu3thZ14DkJ9J5wi8TUkD5CaBpEquHR4felREJ+JCaPfg==",
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/@supabase/grid/-/grid-2.4.3.tgz",
"integrity": "sha512-CfWh3m12eIUWOGYyY/XzTA39fsPEqW4fIaVwPNU770gW6IvzwoN/LItCqoTEQtEHkCttmGuMc1ZzG7z7hpJqYA==",
"dependencies": {
"@monaco-editor/react": "^4.3.1",
"@scaleleap/pg-format": "^1.0.0",
@@ -2699,7 +2700,7 @@
},
"peerDependencies": {
"@supabase/react-data-grid": "^7.1.0-beta.7",
"@supabase/ui": "^0.35.0",
"@supabase/ui": "^0.36.4",
"react": "^16.14 || ^17.0.0",
"react-dom": "^16.14 || ^17.0.0"
}
@@ -2747,6 +2748,11 @@
"websocket": "^1.0.34"
}
},
"node_modules/@supabase/shared-types": {
"version": "0.1.15",
"resolved": "https://registry.npmjs.org/@supabase/shared-types/-/shared-types-0.1.15.tgz",
"integrity": "sha512-0IJqw0nU526NTxKnAW4N3eNNU9PSG961fXFS3BrguFPmhCUGuapW6zJOZM1FVFeMpn7y6pWw0iV7UqbRoiy8sg=="
},
"node_modules/@supabase/storage-js": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-1.5.0.tgz",
@@ -2767,9 +2773,9 @@
}
},
"node_modules/@supabase/ui": {
"version": "0.35.0",
"resolved": "https://registry.npmjs.org/@supabase/ui/-/ui-0.35.0.tgz",
"integrity": "sha512-bftQoDcX2Vl6Gcu6IYUjteN7ZKNs5YhvhOz3c/8kTYU6DuD33VKOYCMQBKwU4IuBbEsNXVRlJxHK4RjXIiS4ww==",
"version": "0.36.4",
"resolved": "https://registry.npmjs.org/@supabase/ui/-/ui-0.36.4.tgz",
"integrity": "sha512-7Wq4D/5269q1wpmSo8V57hhWEXB83eKKEW9NHBdSS7wnsAb9hK+m6D/DN8ImpSdybDheHOdT/C4xjc2W6luOxg==",
"dependencies": {
"lodash": "^4.17.20",
"prop-types": "^15.7.2"
@@ -4831,14 +4837,6 @@
"node": ">= 0.6"
}
},
"node_modules/dequal": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.2.tgz",
"integrity": "sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug==",
"engines": {
"node": ">=6"
}
},
"node_modules/des.js": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
@@ -12212,14 +12210,11 @@
}
},
"node_modules/swr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/swr/-/swr-1.0.1.tgz",
"integrity": "sha512-EPQAxSjoD4IaM49rpRHK0q+/NzcwoT8c0/Ylu/u3/6mFj/CWnQVjNJ0MV2Iuw/U+EJSd2TX5czdAwKPYZIG0YA==",
"dependencies": {
"dequal": "2.0.2"
},
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/swr/-/swr-1.2.1.tgz",
"integrity": "sha512-1cuWXqJqXcFwbgONGCY4PHZ8v05009JdHsC3CIC6u7d00kgbMswNr1sHnnhseOBxtzVqcCNpOHEgVDciRer45w==",
"peerDependencies": {
"react": "^16.11.0 || ^17.0.0"
"react": "^16.11.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/symbol-tree": {
@@ -15444,9 +15439,9 @@
}
},
"@supabase/grid": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@supabase/grid/-/grid-2.4.1.tgz",
"integrity": "sha512-KDlm8wSwGqX/UvGMOnKwMSJjq3vlKScSyNNfLnr1UMu3thZ14DkJ9J5wi8TUkD5CaBpEquHR4felREJ+JCaPfg==",
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/@supabase/grid/-/grid-2.4.3.tgz",
"integrity": "sha512-CfWh3m12eIUWOGYyY/XzTA39fsPEqW4fIaVwPNU770gW6IvzwoN/LItCqoTEQtEHkCttmGuMc1ZzG7z7hpJqYA==",
"requires": {
"@monaco-editor/react": "^4.3.1",
"@scaleleap/pg-format": "^1.0.0",
@@ -15498,6 +15493,11 @@
"websocket": "^1.0.34"
}
},
"@supabase/shared-types": {
"version": "0.1.15",
"resolved": "https://registry.npmjs.org/@supabase/shared-types/-/shared-types-0.1.15.tgz",
"integrity": "sha512-0IJqw0nU526NTxKnAW4N3eNNU9PSG961fXFS3BrguFPmhCUGuapW6zJOZM1FVFeMpn7y6pWw0iV7UqbRoiy8sg=="
},
"@supabase/storage-js": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-1.5.0.tgz",
@@ -15518,9 +15518,9 @@
}
},
"@supabase/ui": {
"version": "0.35.0",
"resolved": "https://registry.npmjs.org/@supabase/ui/-/ui-0.35.0.tgz",
"integrity": "sha512-bftQoDcX2Vl6Gcu6IYUjteN7ZKNs5YhvhOz3c/8kTYU6DuD33VKOYCMQBKwU4IuBbEsNXVRlJxHK4RjXIiS4ww==",
"version": "0.36.4",
"resolved": "https://registry.npmjs.org/@supabase/ui/-/ui-0.36.4.tgz",
"integrity": "sha512-7Wq4D/5269q1wpmSo8V57hhWEXB83eKKEW9NHBdSS7wnsAb9hK+m6D/DN8ImpSdybDheHOdT/C4xjc2W6luOxg==",
"requires": {
"fsevents": "^2.3.2",
"lodash": "^4.17.20",
@@ -17276,11 +17276,6 @@
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"dequal": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.2.tgz",
"integrity": "sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug=="
},
"des.js": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
@@ -22860,12 +22855,10 @@
}
},
"swr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/swr/-/swr-1.0.1.tgz",
"integrity": "sha512-EPQAxSjoD4IaM49rpRHK0q+/NzcwoT8c0/Ylu/u3/6mFj/CWnQVjNJ0MV2Iuw/U+EJSd2TX5czdAwKPYZIG0YA==",
"requires": {
"dequal": "2.0.2"
}
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/swr/-/swr-1.2.1.tgz",
"integrity": "sha512-1cuWXqJqXcFwbgONGCY4PHZ8v05009JdHsC3CIC6u7d00kgbMswNr1sHnnhseOBxtzVqcCNpOHEgVDciRer45w==",
"requires": {}
},
"symbol-tree": {
"version": "3.2.4",
+4 -3
View File
@@ -17,11 +17,12 @@
"@radix-ui/react-tooltip": "^0.1.6",
"@sentry/browser": "^6.15.0",
"@sentry/nextjs": "^6.15.0",
"@supabase/grid": "^2.4.1",
"@supabase/grid": "^2.4.3",
"@supabase/postgres-meta": "^0.26.1",
"@supabase/react-data-grid": "^7.1.0-beta.7",
"@supabase/shared-types": "^0.1.15",
"@supabase/supabase-js": "^1.29.1",
"@supabase/ui": "^0.35.0",
"@supabase/ui": "^0.36.4",
"ajv": "^8.6.3",
"analytics-node": "^3.5.0",
"clipboard": "^2.0.8",
@@ -55,7 +56,7 @@
"sass": "^1.43.4",
"semver": "^6.3.0",
"sqlstring": "^2.3.2",
"swr": "^1.0.1",
"swr": "^1.2.1",
"uniforms-bootstrap4": "^3.6.2",
"uniforms-bridge-json-schema": "^3.6.2",
"zxcvbn": "^4.4.2"
+1 -1
View File
@@ -82,7 +82,7 @@ const DocView: FC<any> = observer(({}) => {
const { data: jsonSchema, error: jsonSchemaError } = useSWR(
() => swaggerUrl,
(url) => get(url, { headers, credentials: 'omit' }).then((res) => res)
(url: string) => get(url, { headers, credentials: 'omit' }).then((res) => res)
)
useEffect(() => {
+2 -2
View File
@@ -717,7 +717,7 @@ const Settings = () => {
</>
)}
<Divider light />
{/* <ToggleField
<ToggleField
name="EXTERNAL_LINKEDIN_ENABLED"
addOns={
externalProvidersModel.EXTERNAL_LINKEDIN_ENABLED && (
@@ -774,7 +774,7 @@ const Settings = () => {
/>
</>
)}
<Divider light /> */}
<Divider light />
<ToggleField
name="EXTERNAL_TWITCH_ENABLED"
addOns={
+235 -90
View File
@@ -1,20 +1,69 @@
import useSWR from 'swr'
import { FC, createContext, useContext, useState } from 'react'
import {
Dispatch,
FC,
SetStateAction,
createContext,
useContext,
useEffect,
useRef,
useState,
} from 'react'
import { indexOf } from 'lodash'
import { useRouter } from 'next/router'
import { AutoField } from 'uniforms-bootstrap4'
import { observer, useLocalObservable } from 'mobx-react-lite'
import { Typography, Input, IconAlertCircle, Modal, IconKey } from '@supabase/ui'
import {
JwtSecretUpdateError,
JwtSecretUpdateProgress,
JwtSecretUpdateStatus,
} from '@supabase/shared-types/out/events'
import {
Alert,
Typography,
Input,
IconAlertCircle,
Modal,
IconKey,
Button,
Dropdown,
Divider,
IconPenTool,
IconRefreshCw,
IconChevronDown,
IconLoader,
} from '@supabase/ui'
import { API_URL } from 'lib/constants'
import { useStore, withAuth } from 'hooks'
import { uuidv4 } from 'lib/helpers'
import { patch, get } from 'lib/common/fetch'
import { useStore, useJwtSecretUpdateStatus, withAuth } from 'hooks'
import { SettingsLayout } from 'components/layouts'
import Panel from 'components/to-be-cleaned/Panel'
import MultiSelectUI from 'components/to-be-cleaned/MultiSelect'
import SchemaFormPanel from 'components/to-be-cleaned/forms/SchemaFormPanel'
import { DisplayApiSettings } from 'components/to-be-cleaned/DisplayProjectSettings'
import ConfirmationModal from 'components/ui/ConfirmationModal'
import Flag from 'components/ui/Flag/Flag'
const JWT_SECRET_UPDATE_ERROR_MESSAGES = {
[JwtSecretUpdateError.APIServicesConfigurationUpdateFailed]:
'failed to update configuration for API services',
[JwtSecretUpdateError.APIServicesRestartFailed]: 'failed to restart API services',
[JwtSecretUpdateError.DatabaseAdminAPIConfigurationUpdateFailed]:
'failed to update configuration for database admin API',
[JwtSecretUpdateError.PostgreSQLRestartFailed]: 'failed to restart PostgreSQL service',
[JwtSecretUpdateError.SupabaseAPIKeyUpdateFailed]: 'failed to update Supabase API key',
}
const JWT_SECRET_UPDATE_PROGRESS_MESSAGES = {
[JwtSecretUpdateProgress.RestartedAPIServices]: 'restarted API services',
[JwtSecretUpdateProgress.RestartedPostgreSQL]: 'restarted PostgreSQL service',
[JwtSecretUpdateProgress.Started]: 'started updating',
[JwtSecretUpdateProgress.UpdatedAPIServicesConfiguration]:
'updated configuration for API services',
[JwtSecretUpdateProgress.UpdatedDatabaseAdminAPIConfiguration]:
'updated configuration for database admin API',
}
const PageContext: any = createContext(null)
@@ -66,7 +115,8 @@ const ServiceList: FC<any> = ({ projectRef }) => {
const [customToken, setCustomToken] = useState<string>('')
const [isRegeneratingKey, setIsGeneratingKey] = useState<boolean>(false)
const [isCreatingKey, setIsCreatingKey] = useState<boolean>(false)
const [isLoadingCreatingKey, setIsLoadingCreatingKey] = useState<boolean>(false)
const [isSubmittingJwtSecretUpdateRequest, setIsSubmittingJwtSecretUpdateRequest] =
useState<boolean>(false)
const {
data,
@@ -77,15 +127,57 @@ const ServiceList: FC<any> = ({ projectRef }) => {
`${API_URL}/projects/${projectRef}/config?app=postgrest`,
get
)
const {
changeTrackingId,
isError: isJwtSecretUpdateStatusError,
isLoading: isJwtSecretUpdateStatusLoading,
jwtSecretUpdateError,
jwtSecretUpdateProgress,
jwtSecretUpdateStatus,
mutateJwtSecretUpdateStatus,
}: any = useJwtSecretUpdateStatus(ref)
if (error) {
const { Failed, Updated, Updating } = JwtSecretUpdateStatus
const isJwtSecretUpdateFailed = jwtSecretUpdateStatus === Failed
const isNotUpdatingJwtSecret =
jwtSecretUpdateStatus === undefined || jwtSecretUpdateStatus === Updated
const isUpdatingJwtSecret = jwtSecretUpdateStatus === Updating
const jwtSecretUpdateErrorMessage =
JWT_SECRET_UPDATE_ERROR_MESSAGES[jwtSecretUpdateError as JwtSecretUpdateError]
const jwtSecretUpdateProgressMessage =
JWT_SECRET_UPDATE_PROGRESS_MESSAGES[jwtSecretUpdateProgress as JwtSecretUpdateProgress]
const previousJwtSecretUpdateStatus = useRef()
useEffect(() => {
if (previousJwtSecretUpdateStatus.current === Updating) {
switch (jwtSecretUpdateStatus) {
case Updated:
mutateConfig()
mutateSettings()
ui.setNotification({ category: 'success', message: 'Successfully updated JWT secret' })
break
case Failed:
ui.setNotification({
category: 'error',
message: `JWT secret update failed: ${jwtSecretUpdateErrorMessage}`,
})
break
}
}
previousJwtSecretUpdateStatus.current = jwtSecretUpdateStatus
}, [jwtSecretUpdateStatus])
if (error || isJwtSecretUpdateStatusError) {
return (
<div className="p-6 mx-auto sm:w-full md:w-3/4 text-center">
<Typography.Title level={3}>Error loading API settings</Typography.Title>
</div>
)
}
if (!data) {
if (!data || isJwtSecretUpdateStatusLoading) {
return (
<div className="p-6 mx-auto sm:w-full md:w-3/4 text-center">
<Typography.Title level={3}>Loading...</Typography.Title>
@@ -100,38 +192,29 @@ const ServiceList: FC<any> = ({ projectRef }) => {
const apiService = services.find((x: any) => x.app.id == API_SERVICE_ID)
const apiConfig = apiService?.app_config
async function handleGenerateNewJwtToken() {
async function handleJwtSecretUpdate(
jwt_secret: string,
setModalVisibility: Dispatch<SetStateAction<boolean>>
) {
setIsSubmittingJwtSecretUpdateRequest(true)
try {
const trackingId = uuidv4()
const res = await patch(`${API_URL}/projects/${ref}/config?app=secrets`, {
jwt_secret: 'ROLL',
jwt_secret,
change_tracking_id: trackingId,
})
if (res.error) throw res.error
setIsGeneratingKey(false)
// refetch data in forms
mutateSettings()
mutateConfig()
ui.setNotification({ category: 'success', message: 'Successfully updated JWT secret' })
} catch (error: any) {
ui.setNotification({ category: 'error', message: error.message })
}
}
async function handleCustomNewJwtToken() {
setIsLoadingCreatingKey(true)
try {
const res = await patch(`${API_URL}/projects/${ref}/config?app=secrets`, {
jwt_secret: customToken,
setModalVisibility(false)
mutateJwtSecretUpdateStatus()
ui.setNotification({
category: 'info',
message:
'Successfully submitted JWT secret update request. Please wait while your project is updated.',
})
if (res.error) throw res.error
setIsCreatingKey(false)
// refetch data in forms
mutateSettings()
mutateConfig()
ui.setNotification({ category: 'success', message: 'Successfully updated JWT secret' })
} catch (error: any) {
ui.setNotification({ category: 'error', message: error.message })
} finally {
setIsLoadingCreatingKey(false)
setIsSubmittingJwtSecretUpdateRequest(false)
}
}
@@ -163,92 +246,154 @@ const ServiceList: FC<any> = ({ projectRef }) => {
<Input
label="JWT Secret"
readOnly
copy
reveal
disabled={true}
value={config?.jwt_secret || ''}
copy={isNotUpdatingJwtSecret}
reveal={isNotUpdatingJwtSecret}
disabled
value={
isJwtSecretUpdateFailed
? 'JWT secret update failed'
: isUpdatingJwtSecret
? 'Updating JWT secret...'
: config?.jwt_secret || ''
}
className="input-mono"
descriptionText={
'Used to decode your JWTs. You can also use this to mint your own JWTs.'
}
layout="horizontal"
/>
{/* Temporarily hide the jwt secret rolling feature */}
{/* <div className="space-y-3">
<div className="p-3 px-6 dark:bg-bg-alt-dark bg-bg-alt-light rounded-md shadow-sm border dark:border-dark flex items-center justify-between">
<div>
<Typography.Text>Generate a new JWT secret</Typography.Text>
<div>
<Typography.Text type="secondary">
A random secret will be created, or you can create your own.
</Typography.Text>
</div>
</div>
<div className="flex flex-col items-end">
<Dropdown
align="end"
side="bottom"
overlay={
<>
<Dropdown.Item
onClick={() => setIsGeneratingKey(true)}
icon={<IconRefreshCw size={16} />}
>
Generate a random secret
</Dropdown.Item>
<Divider light />
<Dropdown.Item
onClick={() => setIsCreatingKey(true)}
icon={<IconPenTool size={16} />}
>
Create my own secret
</Dropdown.Item>
</>
}
>
<Button as="span" type="secondary" iconRight={<IconChevronDown />}>
Generate a new secret
</Button>
</Dropdown>
<Flag name="jwtSecretUpdate">
<div className="space-y-3">
<div className="p-3 px-6 dark:bg-bg-alt-dark bg-bg-alt-light rounded-md shadow-sm border dark:border-dark">
{isJwtSecretUpdateFailed ? (
<Alert withIcon variant="warning" title="Failed to update JWT secret">
Please contact Supabase support with the following details: <br />
Change tracking ID: {changeTrackingId} <br />
Error message: {jwtSecretUpdateErrorMessage}
</Alert>
) : (
<>
{isUpdatingJwtSecret ? (
<div className="flex items-center space-x-2">
<IconLoader className="animate-spin" size={14} />
<Typography.Text>
Updating JWT secret: {jwtSecretUpdateProgressMessage}
</Typography.Text>
</div>
) : (
<div className="w-full space-y-2">
<div className="w-full flex items-center justify-between">
<div className="flex flex-col space-y-1">
<Typography.Text>Generate a new JWT secret</Typography.Text>
<Typography.Text type="secondary">
A random secret will be created, or you can create your own.
</Typography.Text>
</div>
<div className="flex flex-col items-end">
{isUpdatingJwtSecret ? (
<Button loading type="secondary">
Updating JWT secret...
</Button>
) : (
<Dropdown
align="end"
side="bottom"
overlay={
<>
<Dropdown.Item
onClick={() => setIsGeneratingKey(true)}
icon={<IconRefreshCw size={16} />}
>
Generate a random secret
</Dropdown.Item>
<Divider light />
<Dropdown.Item
onClick={() => setIsCreatingKey(true)}
icon={<IconPenTool size={16} />}
>
Create my own secret
</Dropdown.Item>
</>
}
>
<Button
as="span"
type="secondary"
iconRight={<IconChevronDown />}
>
Generate a new secret
</Button>
</Dropdown>
)}
</div>
</div>
<Alert
withIcon
variant="warning"
title="This will invalidate all existing API keys!"
>
Your project will also be restarted during this process, which will
terminate any existing connections.
</Alert>
</div>
)}
</>
)}
</div>
</div>
</div> */}
</Flag>
</Panel.Content>
</Panel>
</section>
<section>{config && <PostgrestConfig config={config} projectRef={projectRef} />}</section>
</article>
<ConfirmationModal
danger
<Modal
visible={isRegeneratingKey}
title="Generate new JWT secret"
buttonLabel="Generate new secret"
buttonLoadingLabel="Generating"
children={
<Typography.Text type="secondary">
Are you sure you want to genereate a random new JWT secret? This action cannot be undone
and the old JWT secret will be lost.
</Typography.Text>
}
onSelectCancel={() => setIsGeneratingKey(false)}
onSelectConfirm={handleGenerateNewJwtToken}
/>
onCancel={() => setIsGeneratingKey(false)}
title="Are you absolutely sure?"
icon={<IconAlertCircle background={'red'} />}
hideFooter
size="medium"
closable
>
<Typography.Text>
<p className="text-sm">
This action cannot be undone and the old JWT secret will be lost. All existing API keys
will be invalidated, and any open connections will be terminated.
</p>
</Typography.Text>
<Button
onClick={() => handleJwtSecretUpdate('ROLL', setIsGeneratingKey)}
size="small"
block
danger
loading={isSubmittingJwtSecretUpdateRequest}
>
Generate new secret
</Button>
</Modal>
<Modal
closable
title="Create a custom JWT secret"
visible={isCreatingKey}
size="small"
size="medium"
confirmText="Apply new JWT secret"
variant="danger"
alignFooter="right"
loading={isLoadingCreatingKey}
loading={isSubmittingJwtSecretUpdateRequest}
onCancel={() => setIsCreatingKey(false)}
onConfirm={handleCustomNewJwtToken}
onConfirm={() => handleJwtSecretUpdate(customToken, setIsCreatingKey)}
>
<Typography.Text type="secondary">
Create a custom JWT secret. Make sure it is a strong combination of characters that cannot
be guessed easily.
</Typography.Text>
<Alert
withIcon
variant="warning"
title="All existing API keys will be invalidated, and any open connections will be terminated."
/>
<Input
onChange={(e: any) => setCustomToken(e.target.value)}
value={customToken}
@@ -31,7 +31,7 @@ import {
LOG_TYPE_LABEL_MAPPING,
} from 'components/interfaces/Settings/Logs'
import { uuidv4 } from 'lib/helpers'
import useSWRInfinite from 'swr/infinite'
import useSWRInfinite, { SWRInfiniteKeyLoader } from 'swr/infinite'
import { isUndefined } from 'lodash'
import { useFlag } from 'hooks'
import dayjs from 'dayjs'
@@ -107,7 +107,7 @@ export const LogPage: NextPage = () => {
return qs
}
// handle log fetching
const getKeyLogs: KeyLoader<Logs> = (_pageIndex: number, prevPageData) => {
const getKeyLogs: SWRInfiniteKeyLoader = (_pageIndex: number, prevPageData) => {
let queryParams
// if prev page data is 100 items, could possibly have more records that are not yet fetched within this interval
if (prevPageData === null) {
@@ -268,10 +268,11 @@ export const LogPage: NextPage = () => {
onInputRun={handleRefresh}
/>
</div>
<div className="flex flex-row justify-between items-center px-2 py-1 w-full">
<div className="flex flex-row justify-end items-center px-2 py-1 w-full">
{isSelectQuery && (
<InformationBox
block
className="shrink mr-auto"
block={false}
size="tiny"
icon={<IconInfo size="tiny" />}
title={`Custom queries are restricted to a ${
@@ -391,6 +391,14 @@
{
"value": "messagebird",
"label": "Messagebird"
},
{
"value": "vonage",
"label": "Vonage"
},
{
"value": "textlocal",
"label": "Textlocal"
}
]
},
+1 -1
View File
@@ -128,7 +128,7 @@ let { data, error } = await supabase
... you can simply define a rule on your database table, `auth.uid() = user_id`, and your request will return the rows which pass the rule, even when you remove the filter from your middleware:
```js
let user = await supabase.from('users').select('user_id, name')
let { data, error } = await supabase.from('users').select('user_id, name')
// console.log(data)
// Still => { id: 'd0714948', name: 'Jane' }
+4 -2
View File
@@ -43,8 +43,8 @@ values={[
```sql
-- Example: enable the "pgtap" extension
create extension pgtap;
-- Example: enable the "pgtap" extension and ensure it is installed
create extension pgtap with schema extensions;
-- Example: disable the "pgtap" extension
drop extension pgtap;
@@ -54,6 +54,8 @@ drop extension pgtap;
Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
To disable an extension you can call `drop extension`.
Enabling few extensions by the SQL code `create extension <extension-name> with schema extensions` may lead to permission issues (e.g. `dblink`, `http`, `pg_cron`).
</TabItem>
</Tabs>
+1 -1
View File
@@ -275,7 +275,7 @@ select * from add_planet('Jakku');
<TabItem value="JS">
```js
const { data, error } = supabase
const { data, error } = await supabase
.rpc('add_planet', { name: 'Jakku' })
```
+3 -3
View File
@@ -8,7 +8,7 @@ By default, Supabase limits the maximum statement execution time to _3 seconds_
### Changing the default timeout
The timeout values were picked as a reasonable default for the majority of use-cases, but can be modified using the [`alter role`](https://www.postgresql.org/docs/13/sql-alterrole.html) statement:
The timeout values were picked as a reasonable default for the majority of use-cases, but can be modified using the [`alter role`](https://www.postgresql.org/docs/current/sql-alterrole.html) statement:
```sql
alter role authenticated set statement_timeout = '15s';
@@ -22,6 +22,6 @@ set statement_timeout to 60000; -- 1 minute in milliseconds
### Statement Optimization
All Supabase projects come with the [`pg_stat_statements`](https://www.postgresql.org/docs/13/pgstatstatements.html) extension installed, which tracks planning and execution statistics for all statements executed against it. These statistics can be used in order to diagnose the performance of your project.
All Supabase projects come with the [`pg_stat_statements`](https://www.postgresql.org/docs/current/pgstatstatements.html) extension installed, which tracks planning and execution statistics for all statements executed against it. These statistics can be used in order to diagnose the performance of your project.
This data can further be used in conjunction with the [`explain`](https://www.postgresql.org/docs/13/using-explain.html) functionality of Postgres to optimize your usage.
This data can further be used in conjunction with the [`explain`](https://www.postgresql.org/docs/current/using-explain.html) functionality of Postgres to optimize your usage.
+1 -1
View File
@@ -401,7 +401,7 @@ Write the following and click `Run`.
```sql
create or replace function auth.user_id() returns text as $$
select nullif(current_setting('request.jwt.claims.userId', true), '')::text;
select nullif(current_setting('request.jwt.claims', true)::json->>'userId', '')::text;
$$ language sql stable;
```
@@ -1,4 +1,4 @@
import { useState } from 'react'
import { useEffect, useState } from 'react'
import { createClient } from '@supabase/supabase-js'
import {
Badge,
@@ -17,17 +17,15 @@ import { Swiper, SwiperSlide } from 'swiper/react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useTheme } from '../Providers'
const supabase = createClient(
'https://rsnibhkhsbfnncjmwnkj.supabase.co',
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYxNTIxNDE1MywiZXhwIjoxOTMwNzkwMTUzfQ.OQEbAaTfgDdLCCht251P2JRD3QDnui6nsU8N-tZA_Mc'
)
type Props = {
darkMode: boolean
}
function AuthComponentExample(props: Props) {
const { darkMode } = props
function AuthComponentExample() {
const { isDarkMode } = useTheme()
const { basePath } = useRouter()
// store API swiper instance
@@ -91,9 +89,9 @@ function AuthComponentExample(props: Props) {
<Space size={3} direction="vertical">
<img
src={
darkMode
? `${basePath}/brand-assets/supabase-logo-wordmark--light.svg`
: `${basePath}/brand-assets/supabase-logo-wordmark--dark.svg`
isDarkMode
? `${basePath}/brand-assets/supabase-logo-wordmark--dark.svg`
: `${basePath}/brand-assets/supabase-logo-wordmark--light.svg`
}
width="96"
alt="Logo"
+13 -12
View File
@@ -1,9 +1,4 @@
import { useRouter } from 'next/router'
interface Props {
darkMode: boolean
updateTheme: Function
}
import { useTheme } from '~/components/Providers'
const SunEmoji = () => (
<svg width="32" height="32" fill="none">
@@ -44,12 +39,15 @@ const MoonEmoji = () => (
</svg>
)
function DarkModeToggle(props: Props) {
const { darkMode, updateTheme } = props
function DarkModeToggle() {
const { isDarkMode, toggleTheme } = useTheme()
const toggleDarkMode = () => {
localStorage.setItem('supabaseDarkMode', (!darkMode).toString())
updateTheme(!darkMode)
localStorage.setItem('supabaseDarkMode', (!isDarkMode).toString())
toggleTheme()
const key = localStorage.getItem('supabaseDarkMode')
document.documentElement.className = key === 'true' ? 'dark' : ''
}
return (
@@ -61,7 +59,7 @@ function DarkModeToggle(props: Props) {
className={`
relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer
transition-colors ease-in-out duration-200 focus:outline-none ${
darkMode ? 'bg-dark-500' : 'bg-gray-200'
isDarkMode ? 'bg-dark-500' : 'bg-gray-200'
} mx-5
`}
onClick={() => toggleDarkMode()}
@@ -70,7 +68,9 @@ function DarkModeToggle(props: Props) {
<span
aria-hidden="true"
className={`
${darkMode ? 'translate-x-5' : 'translate-x-0'} inline-block h-5 w-5 rounded-full
${
isDarkMode ? 'translate-x-5' : 'translate-x-0'
} inline-block h-5 w-5 rounded-full
bg-white dark:bg-dark-700 shadow-lg transform ring-0 transition ease-in-out duration-200
`}
/>
@@ -79,4 +79,5 @@ function DarkModeToggle(props: Props) {
</div>
)
}
export default DarkModeToggle
+21 -10
View File
@@ -3,15 +3,11 @@ import FooterLinks from 'data/Footer.json'
import SectionContainer from '../Layouts/SectionContainer'
import DarkModeToggle from '../DarkModeToggle'
import Link from 'next/link'
import { useTheme } from '../Providers'
type Props = {
darkMode: boolean
updateTheme: Function
}
const Footer = (props: Props) => {
const Footer = () => {
const { basePath } = useRouter()
const { darkMode, updateTheme } = props
const { isDarkMode } = useTheme()
return (
<footer
@@ -29,7 +25,7 @@ const Footer = (props: Props) => {
<img
className="w-40"
src={
darkMode
isDarkMode
? `${basePath}/brand-assets/supabase-logo-wordmark--dark.svg`
: `${basePath}/brand-assets/supabase-logo-wordmark--light.svg`
}
@@ -37,7 +33,7 @@ const Footer = (props: Props) => {
/>
</a>
</Link>
<div className="flex space-x-6">
<div className="flex space-x-5">
<a href="https://twitter.com/supabase" className="text-gray-300 hover:text-gray-400">
<span className="sr-only">Twitter</span>
<svg className="h-6 w-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
@@ -66,6 +62,21 @@ const Footer = (props: Props) => {
/>
</svg>
</a>
<a
href="https://youtube.com/c/supabase"
className="text-gray-300 hover:text-gray-400"
>
<span className="sr-only">Youtube</span>
<svg
className="h-6 w-6"
fill="currentColor"
viewBox="0 0 576 512"
aria-hidden="true"
>
<path d="M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z" />
</svg>
</a>
</div>
</div>
<div className="mt-12 grid grid-cols-1 gap-8 xl:mt-0 xl:col-span-2">
@@ -105,7 +116,7 @@ const Footer = (props: Props) => {
<p className="mb-0 self-center text-base text-gray-400 dark:text-dark-400">
&copy; Supabase Inc
</p>
<DarkModeToggle darkMode={darkMode} updateTheme={updateTheme} />
<DarkModeToggle />
</div>
</SectionContainer>
</footer>
+5 -14
View File
@@ -1,4 +1,4 @@
import { useState, useEffect } from 'react'
import { useEffect } from 'react'
import Nav from 'components/Nav/index'
import Footer from 'components/Footer/index'
@@ -10,28 +10,19 @@ type Props = {
const DefaultLayout = (props: Props) => {
const { hideHeader = false, hideFooter = false, children } = props
const [darkMode, setDarkMode] = useState<boolean>(true)
useEffect(() => {
const isDarkMode = localStorage.getItem('supabaseDarkMode')
if (isDarkMode) {
setDarkMode(isDarkMode === 'true')
document.documentElement.className = isDarkMode === 'true' ? 'dark' : ''
}
const key = localStorage.getItem('supabaseDarkMode')
document.documentElement.className = key === 'true' ? 'dark' : ''
}, [])
const updateTheme = (isDarkMode: boolean) => {
document.documentElement.className = isDarkMode ? 'dark' : ''
setDarkMode(isDarkMode)
}
return (
<>
{!hideHeader && <Nav darkMode={darkMode} />}
{!hideHeader && <Nav />}
<div className="min-h-screen bg-white dark:bg-gray-800">
<main>{children}</main>
</div>
{!hideFooter && <Footer darkMode={darkMode} updateTheme={updateTheme} />}
{!hideFooter && <Footer />}
</>
)
}
+4 -6
View File
@@ -12,13 +12,11 @@ import Solutions from '~/components/Nav/Product'
import Developers from '~/components/Nav/Developers'
import Announcement from '~/components/Nav/Announcement'
type Props = {
darkMode: boolean
}
import { useTheme } from '~/components/Providers'
const Nav = (props: Props) => {
const Nav = () => {
const { basePath } = useRouter()
const { darkMode } = props
const { isDarkMode } = useTheme()
const [open, setOpen] = useState(false)
const [openProduct, setOpenProduct] = useState(false)
@@ -199,7 +197,7 @@ const Nav = (props: Props) => {
<img
className="block h-6 w-auto"
src={
darkMode
isDarkMode
? `${basePath}/brand-assets/supabase-logo-wordmark--dark.svg`
: `${basePath}/brand-assets/supabase-logo-wordmark--light.svg`
}
+43
View File
@@ -0,0 +1,43 @@
import { createContext, useContext, useEffect, useState } from 'react'
interface UseThemeProps {
isDarkMode?: boolean
toggleTheme: () => void
}
interface ThemeProviderProps {
children?: any
}
export const ThemeContext = createContext<UseThemeProps>({
isDarkMode: true,
toggleTheme: () => {},
})
export const useTheme = () => useContext(ThemeContext)
export const ThemeProvider = ({ children }: ThemeProviderProps) => {
const [isDarkMode, setIsDarkMode] = useState(false)
useEffect(() => {
const key = localStorage.getItem('supabaseDarkMode')
setIsDarkMode(key === 'true')
}, [])
const toggleTheme = () => {
setIsDarkMode(!isDarkMode)
}
return (
<>
<ThemeContext.Provider
value={{
isDarkMode,
toggleTheme,
}}
>
{children}
</ThemeContext.Provider>
</>
)
}
+103 -103
View File
@@ -1841,9 +1841,9 @@
}
},
"node_modules/@next/env": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/env/-/env-12.0.9.tgz",
"integrity": "sha512-oBlkyDop0Stf7MPIzETGv5r0YT/G/weBrknoPOUTaa5qwOeGjuy6gsOVc/SBtrBkOoBmRpD+fFhQJPvmo1mS+g=="
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/env/-/env-12.1.0.tgz",
"integrity": "sha512-nrIgY6t17FQ9xxwH3jj0a6EOiQ/WDHUos35Hghtr+SWN/ntHIQ7UpuvSi0vaLzZVHQWaDupKI+liO5vANcDeTQ=="
},
"node_modules/@next/mdx": {
"version": "10.2.3",
@@ -1855,9 +1855,9 @@
}
},
"node_modules/@next/swc-android-arm64": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.0.9.tgz",
"integrity": "sha512-aVqgsEn5plmUH2X58sjzhHsH/6majucWTMaaBEs7hHO2+GCwCZc7zaLH4XCBMKPES9Yaja8/pYUbvZQE9DqgFw==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.1.0.tgz",
"integrity": "sha512-/280MLdZe0W03stA69iL+v6I+J1ascrQ6FrXBlXGCsGzrfMaGr7fskMa0T5AhQIVQD4nA/46QQWxG//DYuFBcA==",
"cpu": [
"arm64"
],
@@ -1870,9 +1870,9 @@
}
},
"node_modules/@next/swc-darwin-arm64": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.0.9.tgz",
"integrity": "sha512-uAgRKm4a2nVdyBiPPJokvmDD1saugOvxljz9ld2ih0CCg5S9vBhqaj3kPGCQBj9hSu3q+Lng2CHnQqG3ga1jzA==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.0.tgz",
"integrity": "sha512-R8vcXE2/iONJ1Unf5Ptqjk6LRW3bggH+8drNkkzH4FLEQkHtELhvcmJwkXcuipyQCsIakldAXhRbZmm3YN1vXg==",
"cpu": [
"arm64"
],
@@ -1885,9 +1885,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.0.9.tgz",
"integrity": "sha512-fDOs2lZIyrAdU18IxMA5orBPn9qLbOdu55gXSTNZOhyRJ8ugtbUAejsK7OL0boJy0CCHPAdVRXm01Mwk8tZ9RQ==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.0.tgz",
"integrity": "sha512-ieAz0/J0PhmbZBB8+EA/JGdhRHBogF8BWaeqR7hwveb6SYEIJaDNQy0I+ZN8gF8hLj63bEDxJAs/cEhdnTq+ug==",
"cpu": [
"x64"
],
@@ -1900,9 +1900,9 @@
}
},
"node_modules/@next/swc-linux-arm-gnueabihf": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.0.9.tgz",
"integrity": "sha512-/ni0p9DBvATUML9RQ1ycQuf05uOYKdzA6iI8+eRsARjpGbFVUFbge7XPzlj9g2Q9YWgoN8CSjFGnKRlyky5uHA==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.0.tgz",
"integrity": "sha512-njUd9hpl6o6A5d08dC0cKAgXKCzm5fFtgGe6i0eko8IAdtAPbtHxtpre3VeSxdZvuGFh+hb0REySQP9T1ttkog==",
"cpu": [
"arm"
],
@@ -1915,9 +1915,9 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.0.9.tgz",
"integrity": "sha512-AphxilJDf95rUxJDHgM9Ww1DaYXZWqTvoKwXeej/0SgSvICcRZrLaFDrkojdXz0Rxr4igX2OdYR1S4/Hj1jWOQ==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.0.tgz",
"integrity": "sha512-OqangJLkRxVxMhDtcb7Qn1xjzFA3s50EIxY7mljbSCLybU+sByPaWAHY4px97ieOlr2y4S0xdPKkQ3BCAwyo6Q==",
"cpu": [
"arm64"
],
@@ -1930,9 +1930,9 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.0.9.tgz",
"integrity": "sha512-K5jbvNNzF3mRjWmPdxP5Bg87i7FHivfBj/L0KJlxpkLSC8sffBJDmB6jtMnI7wiPj9J6vmLkbGtSosln78xAlQ==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.0.tgz",
"integrity": "sha512-hB8cLSt4GdmOpcwRe2UzI5UWn6HHO/vLkr5OTuNvCJ5xGDwpPXelVkYW/0+C3g5axbDW2Tym4S+MQCkkH9QfWA==",
"cpu": [
"arm64"
],
@@ -1945,9 +1945,9 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.0.9.tgz",
"integrity": "sha512-bJZ9bkMkQzsY+UyWezEZ77GWQ4TzwKeXdayX3U3+aEkL8k5C6eKBXlidWdrhu0teLmaUXIyWerWrLnJzwGXdfw==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.0.tgz",
"integrity": "sha512-OKO4R/digvrVuweSw/uBM4nSdyzsBV5EwkUeeG4KVpkIZEe64ZwRpnFB65bC6hGwxIBnTv5NMSnJ+0K/WmG78A==",
"cpu": [
"x64"
],
@@ -1960,9 +1960,9 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.0.9.tgz",
"integrity": "sha512-SR9p0R+v1T32DTXPVAXZw31pmJAkSDotC6Afy+mfC0xrEL3pp95R8sGXYAAUCEPkQp0MEeUOVy2LrToe92X7hQ==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.0.tgz",
"integrity": "sha512-JohhgAHZvOD3rQY7tlp7NlmvtvYHBYgY0x5ZCecUT6eCCcl9lv6iV3nfu82ErkxNk1H893fqH0FUpznZ/H3pSw==",
"cpu": [
"x64"
],
@@ -1975,9 +1975,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.0.9.tgz",
"integrity": "sha512-mzQ1A8vfHhJrvEy5KJZGZWEByXthyKfWofvFaf+oo/5nJl/0Bz1ODP2ajSmbLG++77Eo2AROgbm9pkW1ucvG2A==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.0.tgz",
"integrity": "sha512-T/3gIE6QEfKIJ4dmJk75v9hhNiYZhQYAoYm4iVo1TgcsuaKLFa+zMPh4056AHiG6n9tn2UQ1CFE8EoybEsqsSw==",
"cpu": [
"arm64"
],
@@ -1990,9 +1990,9 @@
}
},
"node_modules/@next/swc-win32-ia32-msvc": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.0.9.tgz",
"integrity": "sha512-MpD2vj1zjo1u3J3wiz3pEKse19Etz+P0GL6XfQkB/9a84vJQ1JWMaWBjmIdivzZv718Il2pRSSx8hymwPfguYQ==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.0.tgz",
"integrity": "sha512-iwnKgHJdqhIW19H9PRPM9j55V6RdcOo6rX+5imx832BCWzkDbyomWnlzBfr6ByUYfhohb8QuH4hSGEikpPqI0Q==",
"cpu": [
"ia32"
],
@@ -2005,9 +2005,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.0.9.tgz",
"integrity": "sha512-1c/sxp/4Qz4F6rCxiYqAnrmghCOFt5hHZ9Kd+rXFW5Mqev4C4XDOUMHdBH55HgnJZqngYhOE0r/XNkCtsIojig==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.0.tgz",
"integrity": "sha512-aBvcbMwuanDH4EMrL2TthNJy+4nP59Bimn8egqv6GHMVj0a44cU6Au4PjOhLNqEh9l+IpRGBqMTzec94UdC5xg==",
"cpu": [
"x64"
],
@@ -5443,11 +5443,11 @@
"peer": true
},
"node_modules/next": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/next/-/next-12.0.9.tgz",
"integrity": "sha512-omfYqoR/DvbdOIJ6SS1unKJ4mGIxUPs0RPa7wr/Mft22OCKgJhuG+aI9KFYi5ZJBwoFQk1vqaMKpWz5qr+dN0Q==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/next/-/next-12.1.0.tgz",
"integrity": "sha512-s885kWvnIlxsUFHq9UGyIyLiuD0G3BUC/xrH0CEnH5lHEWkwQcHOORgbDF0hbrW9vr/7am4ETfX4A7M6DjrE7Q==",
"dependencies": {
"@next/env": "12.0.9",
"@next/env": "12.1.0",
"caniuse-lite": "^1.0.30001283",
"postcss": "8.4.5",
"styled-jsx": "5.0.0",
@@ -5460,21 +5460,21 @@
"node": ">=12.22.0"
},
"optionalDependencies": {
"@next/swc-android-arm64": "12.0.9",
"@next/swc-darwin-arm64": "12.0.9",
"@next/swc-darwin-x64": "12.0.9",
"@next/swc-linux-arm-gnueabihf": "12.0.9",
"@next/swc-linux-arm64-gnu": "12.0.9",
"@next/swc-linux-arm64-musl": "12.0.9",
"@next/swc-linux-x64-gnu": "12.0.9",
"@next/swc-linux-x64-musl": "12.0.9",
"@next/swc-win32-arm64-msvc": "12.0.9",
"@next/swc-win32-ia32-msvc": "12.0.9",
"@next/swc-win32-x64-msvc": "12.0.9"
"@next/swc-android-arm64": "12.1.0",
"@next/swc-darwin-arm64": "12.1.0",
"@next/swc-darwin-x64": "12.1.0",
"@next/swc-linux-arm-gnueabihf": "12.1.0",
"@next/swc-linux-arm64-gnu": "12.1.0",
"@next/swc-linux-arm64-musl": "12.1.0",
"@next/swc-linux-x64-gnu": "12.1.0",
"@next/swc-linux-x64-musl": "12.1.0",
"@next/swc-win32-arm64-msvc": "12.1.0",
"@next/swc-win32-ia32-msvc": "12.1.0",
"@next/swc-win32-x64-msvc": "12.1.0"
},
"peerDependencies": {
"fibers": ">= 3.1.0",
"node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0",
"node-sass": "^6.0.0 || ^7.0.0",
"react": "^17.0.2 || ^18.0.0-0",
"react-dom": "^17.0.2 || ^18.0.0-0",
"sass": "^1.3.0"
@@ -10525,9 +10525,9 @@
"integrity": "sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA=="
},
"@next/env": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/env/-/env-12.0.9.tgz",
"integrity": "sha512-oBlkyDop0Stf7MPIzETGv5r0YT/G/weBrknoPOUTaa5qwOeGjuy6gsOVc/SBtrBkOoBmRpD+fFhQJPvmo1mS+g=="
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/env/-/env-12.1.0.tgz",
"integrity": "sha512-nrIgY6t17FQ9xxwH3jj0a6EOiQ/WDHUos35Hghtr+SWN/ntHIQ7UpuvSi0vaLzZVHQWaDupKI+liO5vANcDeTQ=="
},
"@next/mdx": {
"version": "10.2.3",
@@ -10536,69 +10536,69 @@
"requires": {}
},
"@next/swc-android-arm64": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.0.9.tgz",
"integrity": "sha512-aVqgsEn5plmUH2X58sjzhHsH/6majucWTMaaBEs7hHO2+GCwCZc7zaLH4XCBMKPES9Yaja8/pYUbvZQE9DqgFw==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.1.0.tgz",
"integrity": "sha512-/280MLdZe0W03stA69iL+v6I+J1ascrQ6FrXBlXGCsGzrfMaGr7fskMa0T5AhQIVQD4nA/46QQWxG//DYuFBcA==",
"optional": true
},
"@next/swc-darwin-arm64": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.0.9.tgz",
"integrity": "sha512-uAgRKm4a2nVdyBiPPJokvmDD1saugOvxljz9ld2ih0CCg5S9vBhqaj3kPGCQBj9hSu3q+Lng2CHnQqG3ga1jzA==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.0.tgz",
"integrity": "sha512-R8vcXE2/iONJ1Unf5Ptqjk6LRW3bggH+8drNkkzH4FLEQkHtELhvcmJwkXcuipyQCsIakldAXhRbZmm3YN1vXg==",
"optional": true
},
"@next/swc-darwin-x64": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.0.9.tgz",
"integrity": "sha512-fDOs2lZIyrAdU18IxMA5orBPn9qLbOdu55gXSTNZOhyRJ8ugtbUAejsK7OL0boJy0CCHPAdVRXm01Mwk8tZ9RQ==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.0.tgz",
"integrity": "sha512-ieAz0/J0PhmbZBB8+EA/JGdhRHBogF8BWaeqR7hwveb6SYEIJaDNQy0I+ZN8gF8hLj63bEDxJAs/cEhdnTq+ug==",
"optional": true
},
"@next/swc-linux-arm-gnueabihf": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.0.9.tgz",
"integrity": "sha512-/ni0p9DBvATUML9RQ1ycQuf05uOYKdzA6iI8+eRsARjpGbFVUFbge7XPzlj9g2Q9YWgoN8CSjFGnKRlyky5uHA==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.0.tgz",
"integrity": "sha512-njUd9hpl6o6A5d08dC0cKAgXKCzm5fFtgGe6i0eko8IAdtAPbtHxtpre3VeSxdZvuGFh+hb0REySQP9T1ttkog==",
"optional": true
},
"@next/swc-linux-arm64-gnu": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.0.9.tgz",
"integrity": "sha512-AphxilJDf95rUxJDHgM9Ww1DaYXZWqTvoKwXeej/0SgSvICcRZrLaFDrkojdXz0Rxr4igX2OdYR1S4/Hj1jWOQ==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.0.tgz",
"integrity": "sha512-OqangJLkRxVxMhDtcb7Qn1xjzFA3s50EIxY7mljbSCLybU+sByPaWAHY4px97ieOlr2y4S0xdPKkQ3BCAwyo6Q==",
"optional": true
},
"@next/swc-linux-arm64-musl": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.0.9.tgz",
"integrity": "sha512-K5jbvNNzF3mRjWmPdxP5Bg87i7FHivfBj/L0KJlxpkLSC8sffBJDmB6jtMnI7wiPj9J6vmLkbGtSosln78xAlQ==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.0.tgz",
"integrity": "sha512-hB8cLSt4GdmOpcwRe2UzI5UWn6HHO/vLkr5OTuNvCJ5xGDwpPXelVkYW/0+C3g5axbDW2Tym4S+MQCkkH9QfWA==",
"optional": true
},
"@next/swc-linux-x64-gnu": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.0.9.tgz",
"integrity": "sha512-bJZ9bkMkQzsY+UyWezEZ77GWQ4TzwKeXdayX3U3+aEkL8k5C6eKBXlidWdrhu0teLmaUXIyWerWrLnJzwGXdfw==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.0.tgz",
"integrity": "sha512-OKO4R/digvrVuweSw/uBM4nSdyzsBV5EwkUeeG4KVpkIZEe64ZwRpnFB65bC6hGwxIBnTv5NMSnJ+0K/WmG78A==",
"optional": true
},
"@next/swc-linux-x64-musl": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.0.9.tgz",
"integrity": "sha512-SR9p0R+v1T32DTXPVAXZw31pmJAkSDotC6Afy+mfC0xrEL3pp95R8sGXYAAUCEPkQp0MEeUOVy2LrToe92X7hQ==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.0.tgz",
"integrity": "sha512-JohhgAHZvOD3rQY7tlp7NlmvtvYHBYgY0x5ZCecUT6eCCcl9lv6iV3nfu82ErkxNk1H893fqH0FUpznZ/H3pSw==",
"optional": true
},
"@next/swc-win32-arm64-msvc": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.0.9.tgz",
"integrity": "sha512-mzQ1A8vfHhJrvEy5KJZGZWEByXthyKfWofvFaf+oo/5nJl/0Bz1ODP2ajSmbLG++77Eo2AROgbm9pkW1ucvG2A==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.0.tgz",
"integrity": "sha512-T/3gIE6QEfKIJ4dmJk75v9hhNiYZhQYAoYm4iVo1TgcsuaKLFa+zMPh4056AHiG6n9tn2UQ1CFE8EoybEsqsSw==",
"optional": true
},
"@next/swc-win32-ia32-msvc": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.0.9.tgz",
"integrity": "sha512-MpD2vj1zjo1u3J3wiz3pEKse19Etz+P0GL6XfQkB/9a84vJQ1JWMaWBjmIdivzZv718Il2pRSSx8hymwPfguYQ==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.0.tgz",
"integrity": "sha512-iwnKgHJdqhIW19H9PRPM9j55V6RdcOo6rX+5imx832BCWzkDbyomWnlzBfr6ByUYfhohb8QuH4hSGEikpPqI0Q==",
"optional": true
},
"@next/swc-win32-x64-msvc": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.0.9.tgz",
"integrity": "sha512-1c/sxp/4Qz4F6rCxiYqAnrmghCOFt5hHZ9Kd+rXFW5Mqev4C4XDOUMHdBH55HgnJZqngYhOE0r/XNkCtsIojig==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.0.tgz",
"integrity": "sha512-aBvcbMwuanDH4EMrL2TthNJy+4nP59Bimn8egqv6GHMVj0a44cU6Au4PjOhLNqEh9l+IpRGBqMTzec94UdC5xg==",
"optional": true
},
"@nodelib/fs.scandir": {
@@ -13241,22 +13241,22 @@
"peer": true
},
"next": {
"version": "12.0.9",
"resolved": "https://registry.npmjs.org/next/-/next-12.0.9.tgz",
"integrity": "sha512-omfYqoR/DvbdOIJ6SS1unKJ4mGIxUPs0RPa7wr/Mft22OCKgJhuG+aI9KFYi5ZJBwoFQk1vqaMKpWz5qr+dN0Q==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/next/-/next-12.1.0.tgz",
"integrity": "sha512-s885kWvnIlxsUFHq9UGyIyLiuD0G3BUC/xrH0CEnH5lHEWkwQcHOORgbDF0hbrW9vr/7am4ETfX4A7M6DjrE7Q==",
"requires": {
"@next/env": "12.0.9",
"@next/swc-android-arm64": "12.0.9",
"@next/swc-darwin-arm64": "12.0.9",
"@next/swc-darwin-x64": "12.0.9",
"@next/swc-linux-arm-gnueabihf": "12.0.9",
"@next/swc-linux-arm64-gnu": "12.0.9",
"@next/swc-linux-arm64-musl": "12.0.9",
"@next/swc-linux-x64-gnu": "12.0.9",
"@next/swc-linux-x64-musl": "12.0.9",
"@next/swc-win32-arm64-msvc": "12.0.9",
"@next/swc-win32-ia32-msvc": "12.0.9",
"@next/swc-win32-x64-msvc": "12.0.9",
"@next/env": "12.1.0",
"@next/swc-android-arm64": "12.1.0",
"@next/swc-darwin-arm64": "12.1.0",
"@next/swc-darwin-x64": "12.1.0",
"@next/swc-linux-arm-gnueabihf": "12.1.0",
"@next/swc-linux-arm64-gnu": "12.1.0",
"@next/swc-linux-arm64-musl": "12.1.0",
"@next/swc-linux-x64-gnu": "12.1.0",
"@next/swc-linux-x64-musl": "12.1.0",
"@next/swc-win32-arm64-msvc": "12.1.0",
"@next/swc-win32-ia32-msvc": "12.1.0",
"@next/swc-win32-x64-msvc": "12.1.0",
"caniuse-lite": "^1.0.30001283",
"postcss": "8.4.5",
"styled-jsx": "5.0.0",
+6 -8
View File
@@ -6,13 +6,11 @@ import { Button, Typography } from '@supabase/ui'
import DefaultLayout from '../components/Layouts/Default'
type Props = {
darkMode: boolean
}
import { useTheme } from '~/components/Providers'
const Error404 = (props: Props) => {
const Error404 = () => {
const [show404, setShow404] = useState<boolean>(false)
const { darkMode } = props
const { isDarkMode } = useTheme()
useEffect(() => {
setTimeout(() => {
@@ -30,9 +28,9 @@ const Error404 = (props: Props) => {
<a href="/">
<Image
src={
darkMode
? '/images/supabase-logo-wordmark--light.svg'
: '/images/supabase-logo-wordmark--dark.svg'
isDarkMode
? '/images/supabase-logo-wordmark--dark.svg'
: '/images/supabase-logo-wordmark--light.svg'
}
alt=""
height={24}
+4 -1
View File
@@ -6,6 +6,7 @@ import { useEffect } from 'react'
import Meta from '~/components/Favicons'
import '../styles/index.css'
import { post } from './../lib/fetchWrapper'
import { ThemeProvider } from '~/components/Providers'
export default function MyApp({ Component, pageProps }: AppProps) {
const router = useRouter()
@@ -66,7 +67,9 @@ export default function MyApp({ Component, pageProps }: AppProps) {
cardType: 'summary_large_image',
}}
/>
<Component {...pageProps} />
<ThemeProvider>
<Component {...pageProps} />
</ThemeProvider>
</>
)
}
+2 -8
View File
@@ -30,13 +30,7 @@ import GithubExamples from '~/components/Sections/GithubExamples'
import ProductHeader from '~/components/Sections/ProductHeader'
import AuthProviders from '~/data/auth.json'
type Props = {
darkMode: boolean
}
function AuthPage(props: Props) {
const { darkMode } = props
function AuthPage() {
// base path for images
const { basePath } = useRouter()
@@ -262,7 +256,7 @@ function AuthPage(props: Props) {
<SectionContainer>
<div className="grid grid-cols-12 lg:gap-16">
<div className="order-last col-span-12 lg:order-first lg:col-span-6 mt-8 lg:mt-0">
<AuthComponentExample darkMode={darkMode} />
<AuthComponentExample />
</div>
<div className="col-span-12 lg:col-span-6 lg:col-start-7 xl:col-span-4 xl:col-start-8">
<Space className="mb-4">