create reserved usernames

This commit is contained in:
keyan 2021-05-21 19:09:11 -05:00
parent 4f627e2a5c
commit a9ea341a7b
8 changed files with 671 additions and 28 deletions

View File

@ -1,4 +1,4 @@
import { AuthenticationError } from 'apollo-server-errors' import { AuthenticationError, UserInputError } from 'apollo-server-errors'
export default { export default {
Query: { Query: {
@ -18,6 +18,23 @@ export default {
} }
}, },
Mutation: {
setName: async (parent, { name }, { me, models }) => {
if (!me) {
throw new AuthenticationError('you must be logged in')
}
try {
await models.user.update({ where: { name: me.name }, data: { name } })
} catch (error) {
if (error.code === 'P2002') {
throw new UserInputError('name taken')
}
throw error
}
}
},
User: { User: {
nitems: async (user, args, { models }) => { nitems: async (user, args, { models }) => {
return await models.item.count({ where: { userId: user.id, parentId: null } }) return await models.item.count({ where: { userId: user.id, parentId: null } })

View File

@ -8,6 +8,10 @@ export default gql`
nameAvailable(name: String!): Boolean! nameAvailable(name: String!): Boolean!
} }
extend type Mutation {
setName(name: String!): Boolean
}
type User { type User {
id: ID! id: ID!
name: String name: String

View File

@ -84,7 +84,7 @@ export function Input ({ label, prepend, append, hint, showValid, noBottomMargin
} }
export function Form ({ export function Form ({
initial, schema, onSubmit, children, initialError, ...props initial, schema, onSubmit, children, initialError, validateOnBlur, ...props
}) { }) {
const [error, setError] = useState(initialError) const [error, setError] = useState(initialError)
@ -92,7 +92,7 @@ export function Form ({
<Formik <Formik
initialValues={initial} initialValues={initial}
validationSchema={schema} validationSchema={schema}
validateOnBlur={false} validateOnBlur={validateOnBlur}
onSubmit={async (...args) => onSubmit={async (...args) =>
onSubmit && onSubmit(...args).catch(e => setError(e.message || e))} onSubmit && onSubmit(...args).catch(e => setError(e.message || e))}
> >

View File

@ -7,7 +7,7 @@ import { useState } from 'react'
import { Form, Input, SubmitButton } from './form' import { Form, Input, SubmitButton } from './form'
import InputGroup from 'react-bootstrap/InputGroup' import InputGroup from 'react-bootstrap/InputGroup'
import * as Yup from 'yup' import * as Yup from 'yup'
import { gql, useApolloClient, useQuery } from '@apollo/client' import { gql, useApolloClient, useMutation } from '@apollo/client'
const NAME_QUERY = const NAME_QUERY =
gql` gql`
@ -16,11 +16,19 @@ gql`
} }
` `
const NAME_MUTATION =
gql`
mutation setName($name: String!) {
setName(name: $name)
}
`
export default function UserHeader ({ user }) { export default function UserHeader ({ user }) {
const [editting, setEditting] = useState(false) const [editting, setEditting] = useState(false)
const [session] = useSession() const [session] = useSession()
const router = useRouter() const router = useRouter()
const client = useApolloClient() const client = useApolloClient()
const [setName] = useMutation(NAME_MUTATION)
const Satistics = () => <h1 className='ml-2'><small className='text-success'>[{user.stacked} stacked, {user.sats} sats]</small></h1> const Satistics = () => <h1 className='ml-2'><small className='text-success'>[{user.stacked} stacked, {user.sats} sats]</small></h1>
@ -50,6 +58,17 @@ export default function UserHeader ({ user }) {
initial={{ initial={{
name: user.name name: user.name
}} }}
onSubmit={async ({ name }) => {
if (name === user.name) {
setEditting(false)
return
}
const { error } = await setName({ variables: { name } })
if (error) {
throw new Error({ message: error.toString() })
}
window.location = `/${name}`
}}
> >
<Input <Input
prepend=<InputGroup.Text>@</InputGroup.Text> prepend=<InputGroup.Text>@</InputGroup.Text>

View File

@ -1,22 +0,0 @@
import { gql, useMutation, useQuery } from '@apollo/client'
import { Button } from 'react-bootstrap'
import Layout from '../components/layout'
export default function Index () {
const [createAccount] = useMutation(gql`
mutation {
createAccount
}`)
const { data } = useQuery(gql`
{
accounts
}`)
return (
<Layout>
<Button onClick={createAccount}>create account</Button>
{data && data.accounts.map(account =>
<div key={account}>account</div>
)}
</Layout>
)
}

View File

@ -2,7 +2,7 @@
CREATE TABLE "users" ( CREATE TABLE "users" (
"id" SERIAL NOT NULL, "id" SERIAL NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL, "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"name" TEXT, "name" TEXT,
"email" TEXT, "email" TEXT,
"email_verified" TIMESTAMP(3), "email_verified" TIMESTAMP(3),

View File

@ -0,0 +1,625 @@
/*
Warnings:
- Made the column `name` on table `users` required. This step will fail if there are existing NULL values in that column.
*/
-- AlterTable
ALTER TABLE "users" ALTER COLUMN "name" SET NOT NULL;
INSERT INTO "users" ("name") VALUES
('0'),
('about'),
('access'),
('account'),
('accounts'),
('activate'),
('activities'),
('activity'),
('ad'),
('add'),
('address'),
('adm'),
('admin'),
('administration'),
('administrator'),
('ads'),
('adult'),
('advertising'),
('affiliate'),
('affiliates'),
('ajax'),
('all'),
('alpha'),
('analysis'),
('analytics'),
('android'),
('anon'),
('anonymous'),
('api'),
('app'),
('apps'),
('archive'),
('archives'),
('article'),
('asct'),
('asset'),
('atom'),
('auth'),
('authentication'),
('avatar'),
('backup'),
('balancer-manager'),
('banner'),
('banners'),
('beta'),
('billing'),
('bin'),
('bitcoin'),
('blog'),
('blogs'),
('board'),
('book'),
('bookmark'),
('bot'),
('bots'),
('bug'),
('business'),
('cache'),
('cadastro'),
('calendar'),
('call'),
('campaign'),
('cancel'),
('captcha'),
('career'),
('careers'),
('cart'),
('categories'),
('category'),
('cgi'),
('cgi-bin'),
('changelog'),
('chat'),
('check'),
('checking'),
('checkout'),
('client'),
('cliente'),
('clients'),
('code'),
('codereview'),
('comercial'),
('comment'),
('comments'),
('communities'),
('community'),
('company'),
('compare'),
('compras'),
('config'),
('configuration'),
('connect'),
('contact'),
('contact-us'),
('contact_us'),
('contactus'),
('contest'),
('contribute'),
('corp'),
('create'),
('css'),
('dashboard'),
('data'),
('db'),
('default'),
('delete'),
('demo'),
('design'),
('designer'),
('destroy'),
('dev'),
('devel'),
('developer'),
('developers'),
('diagram'),
('diary'),
('dict'),
('dictionary'),
('die'),
('dir'),
('direct_messages'),
('directory'),
('dist'),
('doc'),
('docs'),
('documentation'),
('domain'),
('download'),
('downloads'),
('ecommerce'),
('edit'),
('editor'),
('edu'),
('education'),
('email'),
('employment'),
('empty'),
('end'),
('enterprise'),
('entries'),
('entry'),
('error'),
('errors'),
('eval'),
('event'),
('exit'),
('explore'),
('facebook'),
('faq'),
('favorite'),
('favorites'),
('feature'),
('features'),
('feed'),
('feedback'),
('feeds'),
('file'),
('files'),
('first'),
('flash'),
('fleet'),
('fleets'),
('flog'),
('follow'),
('followers'),
('following'),
('forgot'),
('form'),
('forum'),
('forums'),
('founder'),
('free'),
('friend'),
('friends'),
('ftp'),
('gadget'),
('gadgets'),
('game'),
('games'),
('get'),
('ghost'),
('gift'),
('gifts'),
('gist'),
('github'),
('graph'),
('group'),
('groups'),
('guest'),
('guests'),
('help'),
('home'),
('homepage'),
('host'),
('hosting'),
('hostmaster'),
('hostname'),
('howto'),
('hpg'),
('html'),
('http'),
('httpd'),
('https'),
('i'),
('iamges'),
('icon'),
('icons'),
('id'),
('idea'),
('ideas'),
('image'),
('images'),
('imap'),
('img'),
('index'),
('indice'),
('info'),
('information'),
('inquiry'),
('instagram'),
('intranet'),
('invitations'),
('invite'),
('invoice'),
('invoices'),
('ipad'),
('iphone'),
('irc'),
('is'),
('issue'),
('issues'),
('it'),
('item'),
('items'),
('java'),
('javascript'),
('job'),
('jobs'),
('join'),
('js'),
('json'),
('jump'),
('knowledgebase'),
('language'),
('languages'),
('last'),
('ldap-status'),
('legal'),
('license'),
('link'),
('links'),
('linux'),
('lightning'),
('list'),
('lists'),
('log'),
('log-in'),
('log-out'),
('log_in'),
('log_out'),
('login'),
('logout'),
('logs'),
('m'),
('mac'),
('mail'),
('mail1'),
('mail2'),
('mail3'),
('mail4'),
('mail5'),
('mailer'),
('mailing'),
('maintenance'),
('manager'),
('manual'),
('map'),
('maps'),
('marketing'),
('master'),
('me'),
('media'),
('member'),
('members'),
('message'),
('messages'),
('messenger'),
('microblog'),
('microblogs'),
('mine'),
('mis'),
('mob'),
('mobile'),
('movie'),
('movies'),
('mp3'),
('msg'),
('msn'),
('music'),
('musicas'),
('mx'),
('my'),
('mysql'),
('name'),
('named'),
('nan'),
('nakamoto'),
('navi'),
('navigation'),
('net'),
('network'),
('new'),
('news'),
('newsletter'),
('nick'),
('nickname'),
('notes'),
('noticias'),
('notification'),
('notifications'),
('notify'),
('ns'),
('ns1'),
('ns10'),
('ns2'),
('ns3'),
('ns4'),
('ns5'),
('ns6'),
('ns7'),
('ns8'),
('ns9'),
('null'),
('oauth'),
('oauth_clients'),
('offer'),
('offers'),
('official'),
('old'),
('online'),
('openid'),
('operator'),
('order'),
('orders'),
('organization'),
('organizations'),
('overview'),
('owner'),
('owners'),
('page'),
('pager'),
('pages'),
('panel'),
('password'),
('payment'),
('perl'),
('phone'),
('photo'),
('photoalbum'),
('photos'),
('php'),
('phpmyadmin'),
('phppgadmin'),
('phpredisadmin'),
('pic'),
('pics'),
('ping'),
('plan'),
('plans'),
('plugin'),
('plugins'),
('policy'),
('pop'),
('pop3'),
('popular'),
('portal'),
('post'),
('postfix'),
('postmaster'),
('posts'),
('pr'),
('premium'),
('press'),
('price'),
('pricing'),
('privacy'),
('privacy-policy'),
('privacy_policy'),
('privacypolicy'),
('private'),
('product'),
('products'),
('profile'),
('project'),
('projects'),
('promo'),
('pub'),
('public'),
('purpose'),
('put'),
('python'),
('query'),
('random'),
('ranking'),
('read'),
('readme'),
('recent'),
('recruit'),
('recruitment'),
('register'),
('registration'),
('release'),
('remove'),
('replies'),
('report'),
('reports'),
('repositories'),
('repository'),
('req'),
('request'),
('requests'),
('reset'),
('roc'),
('root'),
('rss'),
('ruby'),
('rule'),
('sag'),
('sale'),
('sales'),
('sample'),
('samples'),
('sat'),
('sats'),
('satoshi'),
('satoshinakamoto'),
('satoshi_nakamoto'),
('save'),
('school'),
('script'),
('scripts'),
('search'),
('secure'),
('security'),
('self'),
('send'),
('server'),
('server-info'),
('server-status'),
('service'),
('services'),
('session'),
('sessions'),
('setting'),
('settings'),
('setup'),
('share'),
('shop'),
('show'),
('sign-in'),
('sign-up'),
('sign_in'),
('sign_up'),
('signin'),
('signout'),
('signup'),
('site'),
('sitemap'),
('sites'),
('smartphone'),
('smtp'),
('soporte'),
('source'),
('spec'),
('special'),
('sql'),
('src'),
('ssh'),
('ssl'),
('ssladmin'),
('ssladministrator'),
('sslwebmaster'),
('staff'),
('stage'),
('staging'),
('start'),
('stat'),
('state'),
('static'),
('stats'),
('status'),
('store'),
('stores'),
('stories'),
('style'),
('styleguide'),
('stylesheet'),
('stylesheets'),
('subdomain'),
('subscribe'),
('subscriptions'),
('suporte'),
('support'),
('svn'),
('swf'),
('sys'),
('sysadmin'),
('sysadministrator'),
('system'),
('tablet'),
('tablets'),
('tag'),
('talk'),
('task'),
('tasks'),
('team'),
('teams'),
('tech'),
('telnet'),
('term'),
('terms'),
('terms-of-service'),
('terms_of_service'),
('termsofservice'),
('test'),
('test1'),
('test2'),
('test3'),
('teste'),
('testing'),
('tests'),
('theme'),
('themes'),
('thread'),
('threads'),
('tmp'),
('todo'),
('tool'),
('tools'),
('top'),
('topic'),
('topics'),
('tos'),
('tour'),
('translations'),
('trends'),
('tutorial'),
('tux'),
('tv'),
('twitter'),
('undef'),
('unfollow'),
('unsubscribe'),
('update'),
('upload'),
('uploads'),
('url'),
('usage'),
('user'),
('username'),
('users'),
('usuario'),
('vendas'),
('ver'),
('version'),
('video'),
('videos'),
('visitor'),
('wallet'),
('watch'),
('weather'),
('web'),
('webhook'),
('webhooks'),
('webmail'),
('webmaster'),
('website'),
('websites'),
('welcome'),
('widget'),
('widgets'),
('withdrawl'),
('withdrawls'),
('wiki'),
('win'),
('windows'),
('word'),
('work'),
('works'),
('workshop'),
('ww'),
('wws'),
('www'),
('www1'),
('www2'),
('www3'),
('www4'),
('www5'),
('www6'),
('www7'),
('wwws'),
('wwww'),
('xfn'),
('xml'),
('xmpp'),
('xpg'),
('xxx'),
('yaml'),
('year'),
('yml'),
('you'),
('yourdomain'),
('yourname'),
('yoursite'),
('yourusername');

View File

@ -13,7 +13,7 @@ generator client {
model User { model User {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
createdAt DateTime @default(now()) @map(name: "created_at") createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at") updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at")
name String @unique name String @unique
email String? @unique email String? @unique
emailVerified DateTime? @map(name: "email_verified") emailVerified DateTime? @map(name: "email_verified")