Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions pages/_app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import '../app/globals.css';
import { Providers } from '../app/providers';
import EmotionRegistry from '../app/emotion-registry';

function App({ Component, pageProps, authToken }) {
return (
<EmotionRegistry>
<Providers authToken={authToken}>
<Component {...pageProps} />
</Providers>
</EmotionRegistry>
);
}

App.getInitialProps = async ({ ctx }) => {
// Read auth token from cookies on the server
const authToken = ctx.req?.headers.cookie
?.split(';')
.find(c => c.trim().startsWith('spacy_auth='))
?.split('=')[1];

return { authToken };
};

export default App;
3 changes: 3 additions & 0 deletions pages/api/login.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,16 @@ export default async function (req, res) {
error: "Invalid login",
});
} else {
// Decode Relay global ID (["public", "users", uuid]) to get the raw UUID
const userId = JSON.parse(Buffer.from(user.node.id, "base64").toString())[3];
const token = jwt.sign(
{
email,
"https://hasura.io/jwt/claims": {
"x-hasura-allowed-roles": ["user"],
"x-hasura-default-role": "user",
"x-hasura-user-email": email,
"x-hasura-user-id": userId,
},
},
process.env.HASURA_SECRET,
Expand Down
3 changes: 3 additions & 0 deletions pages/api/sign_up.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,16 @@ export default async function (req, res) {
});
}

// Decode Relay global ID (["public", "users", uuid]) to get the raw UUID
const userId = JSON.parse(Buffer.from(result.data.insertUsersOne.id, "base64").toString())[3];
const token = jwt.sign(
{
email: req.body.input.input.email,
"https://hasura.io/jwt/claims": {
"x-hasura-allowed-roles": ["user"],
"x-hasura-default-role": "user",
"x-hasura-user-email": input.email,
"x-hasura-user-id": userId,
},
},
process.env.HASURA_SECRET,
Expand Down
43 changes: 43 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ type Articles implements Node {
createdAt: timestamptz!
id: ID!
intro: String!
slug: String!
title: String!
updatedAt: timestamptz!

"""An object relationship"""
user: Users!
}

"""
Expand All @@ -37,7 +42,10 @@ input ArticlesBoolExp {
createdAt: TimestamptzComparisonExp
id: UuidComparisonExp
intro: StringComparisonExp
slug: StringComparisonExp
title: StringComparisonExp
updatedAt: TimestamptzComparisonExp
user: UsersBoolExp
}

"""
Expand All @@ -56,6 +64,11 @@ enum ArticlesConstraint {
unique or primary key constraint on columns "id"
"""
articles_pkey

"""
unique or primary key constraint on columns "slug"
"""
articles_slug_key
}

type ArticlesEdge {
Expand All @@ -72,7 +85,10 @@ input ArticlesInsertInput {
createdAt: timestamptz
id: uuid
intro: String
slug: String
title: String
updatedAt: timestamptz
user: UsersObjRelInsertInput
}

"""
Expand Down Expand Up @@ -102,7 +118,10 @@ input ArticlesOrderBy {
createdAt: OrderBy
id: OrderBy
intro: OrderBy
slug: OrderBy
title: OrderBy
updatedAt: OrderBy
user: UsersOrderBy
}

"""primary key columns input for table: articles"""
Expand All @@ -129,6 +148,12 @@ enum ArticlesSelectColumn {
"""column name"""
intro

"""column name"""
slug

"""column name"""
title

"""column name"""
updatedAt
}
Expand All @@ -142,6 +167,8 @@ input ArticlesSetInput {
createdAt: timestamptz
id: uuid
intro: String
slug: String
title: String
updatedAt: timestamptz
}

Expand All @@ -164,6 +191,12 @@ enum ArticlesUpdateColumn {
"""column name"""
intro

"""column name"""
slug

"""column name"""
title

"""column name"""
updatedAt
}
Expand Down Expand Up @@ -610,6 +643,16 @@ type UsersMutationResponse {
returning: [Users!]!
}

"""
input type for inserting object relation for remote table "users"
"""
input UsersObjRelInsertInput {
data: UsersInsertInput!

"""upsert condition"""
onConflict: UsersOnConflict
}

"""
on_conflict condition type for table "users"
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,53 @@
table:
name: articles
schema: public
object_relationships:
- name: user
using:
foreign_key_constraint_on: author_id
insert_permissions:
- role: user
permission:
check: {}
set:
author_id: x-hasura-User-Id
columns:
- author_id
- body
- intro
- created_at
- updated_at
- author_id
- id
- intro
- slug
- title
- updated_at
comment: ""
select_permissions:
- role: user
- role: anonymous
permission:
columns:
- body
- intro
- slug
- title
- created_at
- updated_at
- author_id
- id
filter: {}
limit: 0
comment: ""
- role: user
permission:
columns:
- author_id
- body
- created_at
- id
- intro
- slug
- title
- updated_at
filter: {}
comment: ""
update_permissions:
- role: user
Expand All @@ -32,6 +56,7 @@ update_permissions:
- body
- created_at
- intro
- title
- updated_at
filter: {}
check: null
Expand Down
65 changes: 63 additions & 2 deletions src/pages/article/NewArticle.res
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,27 @@

open AncestorSpacy

module CreateArticleMutation = %relay(`
mutation NewArticleMutation($input: ArticlesInsertInput!) {
insertArticlesOne(object: $input) {
id
slug
}
}
`)

module Query = %relay(`
query NewArticleQuery {
usersConnection(first: 1) {
edges {
node {
id
}
}
}
}
`)

module FormFields = %lenses(
type state = {
title: string,
Expand All @@ -26,9 +47,47 @@ let formSchema = {
])
}

let default = () => {
// Helper function to create a URL-friendly slug from a title
let createSlug = title => {
title
->Js.String2.toLowerCase
->Js.String2.replaceByRe(%re("/[^a-z0-9]+/g"), "-")
->Js.String2.replaceByRe(%re("/^-+|-+$/g"), "")
}

@react.component
let make = () => {
let (mutate, _) = CreateArticleMutation.use()
let queryData = Query.use(~variables=(), ())
let user = queryData.usersConnection.edges[0]

let handleSubmit = (event: Form.onSubmitAPI) => {
Js.log(event.state)
let slug = createSlug(event.state.values.title)

mutate(
~variables={
input: {
title: Some(event.state.values.title),
intro: Some(event.state.values.short),
body: Some(event.state.values.content),
slug: Some(slug),
authorId: None,
createdAt: None,
id: None,
updatedAt: None,
user: None
},
},
~onCompleted=(response, _errors) => {
switch response.insertArticlesOne {
| Some(article) =>
// Navigate to the article page
Js.log2("Article created with slug:", article.slug)
| None => Js.log("Failed to create article")
}
},
(),
)->RescriptRelay.Disposable.ignore

None
}
Expand Down Expand Up @@ -84,3 +143,5 @@ let default = () => {
</Stack>
</Stack>
}

let default = make