diff --git a/www/_blog/2022-03-08-audit.mdx b/www/_blog/2022-03-08-audit.mdx index 97d08d91aaf..f067d7f8df5 100644 --- a/www/_blog/2022-03-08-audit.mdx +++ b/www/_blog/2022-03-08-audit.mdx @@ -13,15 +13,14 @@ date: '2022-03-08' toc_depth: 3 --- - -Data auditing is a system that tracks changes to tables' contents over time. +Data auditing is a system that tracks changes to tables' contents over time. PostgreSQL has a robust set of features which we can leverage to create a generic auditing solution in 150 lines of SQL. -Auditing is particularly useful for historical analysis. To demonstrate, imagine you have a `users` table that tracks when a user is online. -You might add a `status` column which can have one of two values: `online` and `offline`. How would you track how long a user is online for throughout an entire month? +Auditing is particularly useful for historical analysis. To demonstrate, imagine you have a `users` table that tracks when a user is online. +You might add a `status` column which can have one of two values: `online` and `offline`. How would you track how long a user is online for throughout an entire month? An auditing system would track every change with timestamps, and so you can measure the difference between each timestamp and sum them up for the entire month. -The goals of our auditing solution are: +The goals of our auditing solution are: - low maintenance - easy to use @@ -60,11 +59,11 @@ select * from audit.record_history ``` ```markdown - id | record_id | old_record_id | op | ts | table_oid | table_schema | table_name | record | old_record +id | record_id | old_record_id | op | ts | table_oid | table_schema | table_name | record | old_record ----+--------------------------------------+--------------------------------------+--------+-------------------------------------+-----------+--------------+------------+--------------------------+-------------------------- - 2 | 1ecd5ff0-1b6b-5bc2-ad80-1cb19769c081 | | INSERT | Mon Feb 28 18:13:52.698511 2022 PST | 16452 | public | members | {"id": 1, "name": "foo"} | - 3 | 1ecd5ff0-1b6b-5bc2-ad80-1cb19769c081 | 1ecd5ff0-1b6b-5bc2-ad80-1cb19769c081 | UPDATE | Mon Feb 28 18:13:52.698511 2022 PST | 16452 | public | members | {"id": 1, "name": "bar"} | {"id": 1, "name": "foo"} - 4 | | 1ecd5ff0-1b6b-5bc2-ad80-1cb19769c081 | DELETE | Mon Feb 28 18:13:52.698511 2022 PST | 16452 | public | members | | {"id": 1, "name": "bar"} +2 | 1ecd5ff0-1b6b-5bc2-ad80-1cb19769c081 | | INSERT | Mon Feb 28 18:13:52.698511 2022 PST | 16452 | public | members | {"id": 1, "name": "foo"} | +3 | 1ecd5ff0-1b6b-5bc2-ad80-1cb19769c081 | 1ecd5ff0-1b6b-5bc2-ad80-1cb19769c081 | UPDATE | Mon Feb 28 18:13:52.698511 2022 PST | 16452 | public | members | {"id": 1, "name": "bar"} | {"id": 1, "name": "foo"} +4 | | 1ecd5ff0-1b6b-5bc2-ad80-1cb19769c081 | DELETE | Mon Feb 28 18:13:52.698511 2022 PST | 16452 | public | members | | {"id": 1, "name": "bar"} (3 rows) ``` @@ -74,10 +73,9 @@ Notice that our `record_id` and `old_record_id` stayed constant as we updated th ### Namespace -To quote a tenet from [the zen of python](https://www.python.org/dev/peps/pep-0020/): +To quote a tenet from [the zen of python](https://www.python.org/dev/peps/pep-0020/): > Namespaces are one honking great idea -- let's do more of those! -> So first things first, we'll create a separate schema named `audit` to house our auditing entities. @@ -89,12 +87,12 @@ create schema if not exists audit; Next, we need a table to track inserts, updates and deletes. -Classically, an audit table's schema mirrors the table being audited and appends some metadata columns like the commit's timestamp. That solution has a few maintenance challenges: +Classically, an audit table's schema mirrors the table being audited and appends some metadata columns like the commit's timestamp. That solution has a few maintenance challenges: - enabling auditing on a table requires a database migration - when the source table's schema changes, the audit table's schema must also change -So instead, we'll lean on PostgreSQL's schema-less [`JSONB` data type](https://www.postgresql.org/docs/current/datatype-json.html) to store each record's data in a single column. +So instead, we'll lean on PostgreSQL's schema-less [`JSONB` data type](https://www.postgresql.org/docs/current/datatype-json.html) to store each record's data in a single column. That approach has the added benefit of allowing us to store multiple tables' audit history in a single audit table. ```sql @@ -110,23 +108,22 @@ create table audit.record_version( table_schema name not null, -- audited table's schema name e.g. 'public' table_name name not null, -- audited table's table name e.g. 'account' -- record data - record jsonb, -- contents of the new record + record jsonb, -- contents of the new record old_record jsonb -- previous record contents (for UPDATE/DELETE) ); ```
-Postgres version compatibility -

-The table above uses PostgreSQL's built-in uuid functionality, which is available from version 14. -For backwards compatibility you can use the uuid-ossp extension. -

-

-create extension if not exists "uuid-ossp"; -

+ Postgres version compatibility +

+ The table above uses PostgreSQL's built-in{' '} + uuid functionality, which + is available from version 14. For backwards compatibility you can use the uuid-ossp extension. +

+

+ create extension if not exists "uuid-ossp"; +

- - ### Query Patterns @@ -134,7 +131,7 @@ An audit log doesn't do us much good if its too slow to query! There are 2 query **Changes to a Table in a Time Range** -For time slices, we need an index on the `ts` column. Since the table is append-only and the `ts` column is populated by insertion date, our values for `ts` are naturally in ascending order. +For time slices, we need an index on the `ts` column. Since the table is append-only and the `ts` column is populated by insertion date, our values for `ts` are naturally in ascending order. PostgreSQL's builtin [BRIN index](https://www.postgresql.org/docs/current/brin-intro.html) can leverage that correlation between value and physical location to produce an index that, at scale, is many hundreds of times smaller than the default (BTREE index) with faster lookup times. @@ -213,7 +210,7 @@ as $$ select uuid_generate_v5( 'fd62bc3d-8d6e-43c2-919c-802ba3762271', - ( + ( jsonb_build_array(to_jsonb($1)) || jsonb_agg($3 ->> key_) )::text diff --git a/www/components/Blog/BlogListItem.tsx b/www/components/Blog/BlogListItem.tsx index d7d1e560454..dfb775284fa 100644 --- a/www/components/Blog/BlogListItem.tsx +++ b/www/components/Blog/BlogListItem.tsx @@ -1,4 +1,3 @@ -import { Space } from '@supabase/ui' import authors from 'lib/authors.json' import React from 'react' import Image from 'next/image' @@ -10,7 +9,20 @@ interface Props { const BlogListItem = ({ post }: Props) => { // @ts-ignore - const author = post.author ? authors[post.author] : authors['supabase'] + const authorArray = post.author && post.author.split(',') + + const author = [] + if (post.author) { + for (let i = 0; i < authorArray.length; i++) { + author.push( + // @ts-ignore + authors.find((authors: string) => { + // @ts-ignore + return authors.author_id === authorArray[i] + }) + ) + } + } return (
@@ -39,17 +51,19 @@ const BlogListItem = ({ post }: Props) => { {post.date &&

{post.date}

}

{post.description}

- {author && !post.hideAuthor ? ( -
- {author.author_image_url && ( - - )} -
- {author.author} - {author.position} + {author.map((author: any) => { + return ( +
+ {author.author_image_url && ( + + )} +
+ {author.author} + {author.position} +
-
- ) : null} + ) + })}
diff --git a/www/components/ExampleCard.tsx b/www/components/ExampleCard.tsx index e9e0d2d496f..bf211f2c418 100644 --- a/www/components/ExampleCard.tsx +++ b/www/components/ExampleCard.tsx @@ -21,7 +21,7 @@ function ExampleCard(props: any) {

{props.description}

- {props.author} + {props.author}
diff --git a/www/components/FeatureColumn.tsx b/www/components/FeatureColumn.tsx index 8ca3e23545c..c2b120180aa 100644 --- a/www/components/FeatureColumn.tsx +++ b/www/components/FeatureColumn.tsx @@ -2,7 +2,7 @@ function FeatureColumn({ icon, title, text }: any) { return ( <> {icon &&
{icon}
} -

{title}

+

{title}

{text}

) diff --git a/www/components/Footer/index.tsx b/www/components/Footer/index.tsx index 3612d39f684..3bb8cc544b6 100644 --- a/www/components/Footer/index.tsx +++ b/www/components/Footer/index.tsx @@ -125,7 +125,7 @@ const Footer = () => {
-

© Supabase Inc

+ © Supabase Inc
diff --git a/www/components/Hero.tsx b/www/components/Hero.tsx index 3f8f0d4e8b1..8b938e8b954 100644 --- a/www/components/Hero.tsx +++ b/www/components/Hero.tsx @@ -23,18 +23,20 @@ const Hero = () => {

The Open Source Firebase Alternative

-

+

Create a backend in less than 2 minutes. Start your project with a Postgres Database, Authentication, instant APIs, Realtime subscriptions and Storage.

-

Serverless functions coming soon

+

Serverless functions coming soon

@@ -50,7 +52,7 @@ const Hero = () => {
- backed by + backed by
{ const { basePath } = useRouter() + const iconSections = Object.values(DevelopersData).map((company: Props) => { const { text, description, url, icon } = company const content = ( - +
{/* */}
-
{text}
-

-

{description}

-

+
{text}
+

{description}

-
+
) return url ? ( { return (
-
-

Latest case studies

+

Latest case studies

    {CaseStudiesData.map((caseStudy: any, idx: number) => { if (idx > 1) { @@ -57,17 +57,7 @@ const Product = () => {
  • { alt="caseStudyThumb" />
    -
  • diff --git a/www/components/Sections/ProductHeader.tsx b/www/components/Sections/ProductHeader.tsx index e40752d52ef..b9665438ae1 100644 --- a/www/components/Sections/ProductHeader.tsx +++ b/www/components/Sections/ProductHeader.tsx @@ -19,7 +19,7 @@ const ProductHeader = (props: Types) => (
    {props.icon || props.title ? ( -
    +
    {props.icon && } {props.title && ( diff --git a/www/components/UI/SectionHeader.tsx b/www/components/UI/SectionHeader.tsx index aca818e6dca..bf71e3dbd2a 100644 --- a/www/components/UI/SectionHeader.tsx +++ b/www/components/UI/SectionHeader.tsx @@ -7,7 +7,7 @@ const SectionHeader = (props: any) => {

    {props.title} - {props.title_alt && {props.title_alt}} + {props.title_alt && {props.title_alt}}

    {props.paragraph &&

    {props.paragraph}

    } diff --git a/www/lib/authors.json b/www/lib/authors.json index 64d7d97e781..edaa5a794da 100644 --- a/www/lib/authors.json +++ b/www/lib/authors.json @@ -1,101 +1,116 @@ -{ - "rory_wilding": { - "author": "Rory Wilding", - "author_url": "https://github.com/roryw10", - "author_image_url": "https://github.com/roryw10.png", - "position": "Head of Growth" - }, - "ant_wilson": { - "author": "Ant Wilson", - "author_url": "https://github.com/awalias", - "author_image_url": "https://github.com/awalias.png", - "position": "CTO and Co-Founder" - }, - "paul_copplestone": { - "author": "Paul Copplestone", - "author_url": "https://github.com/kiwicopple", - "author_image_url": "https://github.com/kiwicopple.png", - "position": "CEO and Co-Founder" - }, - "angelico_de_los_reyes": { +[ + { + "author_id": "angelico_de_los_reyes", "author": "Angelico de los Reyes", + "position": "", "author_url": "https://github.com/dragarcia", - "author_image_url": "https://avatars0.githubusercontent.com/u/26374889?s=400&u=f5e35e9b47a50fa2b4d8c4bb96babd921071bcf1&v=4", - "position": "" + "author_image_url": "https://github.com/dragarcia.png" }, - "thor_schaeff": { - "author": "Thor Schaeff", - "author_url": "https://github.com/thorwebdev", - "author_image_url": "https://github.com/thorwebdev.png", - "position": "DevRel & DX" + { + "author_id": "ant_wilson", + "author": "Ant Wilson", + "position": "CTO and Co-Founder", + "author_url": "https://github.com/awalias", + "author_image_url": "https://github.com/awalias.png" }, - "supabase": { - "author": "Supabase", - "author_url": "https://github.com/supabase", - "author_image_url": "https://github.com/supabase.png", - "position": "" - }, - "inian": { - "author": "Inian Parameshwaran", - "author_url": "https://twitter.com/everConfusedGuy", - "position": "Engineering", - "username": "inian", - "author_image_url": "https://avatars.githubusercontent.com/u/2155545?v=4" - }, - "soedirgo": { + { + "author_id": "soedirgo", "author": "Bobbie Soedirgo", - "author_url": "https://github.com/soedirgo", - "position": "Engineering", "username": "soedirgo", + "position": "Engineering", + "author_url": "https://github.com/soedirgo", "author_image_url": "https://github.com/soedirgo.png" }, - "fracek": { - "author": "Francesco Ceccon", - "author_url": "https://github.com/fracek", - "position": "Engineering", - "username": "fracek", - "author_image_url": "https://github.com/fracek.png" - }, - "steve_chavez": { - "author": "Steve Chavez", - "author_url": "https://github.com/steve-chavez", - "position": "Engineering & PostgREST maintainer", - "username": "steve-chavez", - "author_image_url": "https://github.com/steve-chavez.png" - }, - "kangmingtay": { - "author": "Kang Ming Tay", - "author_url": "https://github.com/kangmingtay", - "position": "Engineering", - "username": "kangmingtay", - "author_image_url": "https://github.com/kangmingtay.png" - }, - "div_arora": { + { + "author_id": "div_arora", "author": "Div Arora", - "author_url": "https://github.com/darora", - "position": "Engineering", "username": "darora", + "position": "Engineering", + "author_url": "https://github.com/darora", "author_image_url": "https://github.com/darora.png" }, - "gurjeet": { - "author": "Gurjeet Singh", - "author_url": "https://github.com/gurjeet", + { + "author_id": "fracek", + "author": "Francesco Ceccon", + "username": "fracek", "position": "Engineering", + "author_url": "https://github.com/fracek", + "author_image_url": "https://github.com/fracek.png" + }, + { + "author_id": "gurjeet", + "author": "Gurjeet Singh", "username": "gurjeet", + "position": "Engineering", + "author_url": "https://github.com/gurjeet", "author_image_url": "https://github.com/gurjeet.png" }, - "wenbo_xie": { - "author": "Wen Bo Xie", - "author_url": "https://github.com/w3b6x9", + { + "author_id": "inian", + "author": "Inian Parameshwaran", + "username": "inian", "position": "Engineering", - "username": "w3b6x9", - "author_image_url": "https://github.com/w3b6x9.png" + "author_url": "https://twitter.com/everConfusedGuy", + "author_image_url": "https://avatars.githubusercontent.com/u/2155545?v=4" }, - "oli_rice": { - "author": "Oliver Rice", - "author_url": "https://github.com/olirice", + { + "author_id": "kangmingtay", + "author": "Kang Ming Tay", + "username": "kangmingtay", "position": "Engineering", + "author_url": "https://github.com/kangmingtay", + "author_image_url": "https://github.com/kangmingtay.png" + }, + { + "author_id": "oli_rice", + "author": "Oliver Rice", "username": "olirice", + "position": "Engineering", + "author_url": "https://github.com/olirice", "author_image_url": "https://github.com/olirice.png" + }, + { + "author_id": "paul_copplestone", + "author": "Paul Copplestone", + "position": "CEO and Co-Founder", + "author_url": "https://github.com/kiwicopple", + "author_image_url": "https://github.com/kiwicopple.png" + }, + { + "author_id": "rory_wilding", + "author": "Rory Wilding", + "position": "Head of Growth", + "author_url": "https://github.com/roryw10", + "author_image_url": "https://github.com/roryw10.png" + }, + { + "author_id": "steve_chavez", + "author": "Steve Chavez", + "username": "steve-chavez", + "position": "Engineering & PostgREST maintainer", + "author_url": "https://github.com/steve-chavez", + "author_image_url": "https://github.com/steve-chavez.png" + }, + { + "author_id": "supabase", + "author": "Supabase", + "position": "", + "author_url": "https://github.com/supabase", + "author_image_url": "https://github.com/supabase.png" + }, + { + "author_id": "thor_schaeff", + "author": "Thor Schaeff", + "position": "DevRel & DX", + "author_url": "https://github.com/thorwebdev", + "author_image_url": "https://github.com/thorwebdev.png" + }, + { + "author_id": "wenbo_xie", + "author": "Wen Bo Xie", + "username": "w3b6x9", + "position": "Engineering", + "author_url": "https://github.com/w3b6x9", + "author_image_url": "https://github.com/w3b6x9.png" } -} +] diff --git a/www/pages/blog.tsx b/www/pages/blog.tsx index 25f40998670..04aff41873e 100644 --- a/www/pages/blog.tsx +++ b/www/pages/blog.tsx @@ -132,13 +132,24 @@ function Blog(props: any) { function FeaturedThumb(blog: PostTypes) { // @ts-ignore - const author = blog.author ? authors[blog.author] : authors['supabase'] + const authorArray = blog.author.split(',') + + const author = [] + for (let i = 0; i < authorArray.length; i++) { + // @ts-ignore + author.push( + authors.find((authors: string) => { + // @ts-ignore + return authors.author_id === authorArray[i] + }) + ) + } return (
    -
    +
    -
    +

    {blog.date}

    •

    {blog.readingTime}

    -

    {blog.title}

    -

    - {blog.description} -

    +

    {blog.title}

    +

    {blog.description}

    - {author && ( -
    - {author.author_image_url && ( -
    - + {author.map((author: any) => { + return ( +
    + {author.author_image_url && ( +
    + +
    + )} +
    + {author.author} + {author.position}
    - )} -
    - {author.author} - {author.position}
    -
    - )} + ) + })}
    diff --git a/www/pages/blog/[year]/[month]/[day]/[slug].tsx b/www/pages/blog/[year]/[month]/[day]/[slug].tsx index 5a57a9e6c3f..bacfd356040 100644 --- a/www/pages/blog/[year]/[month]/[day]/[slug].tsx +++ b/www/pages/blog/[year]/[month]/[day]/[slug].tsx @@ -88,8 +88,19 @@ export async function getStaticProps({ params }: any) { function BlogPostPage(props: any) { // @ts-ignore - const author = props.blog.author ? authors[props.blog.author] : authors['supabase'] const content = hydrate(props.blog.content, { components }) + const authorArray = props.blog.author.split(',') + + const author = [] + for (let i = 0; i < authorArray.length; i++) { + author.push( + // @ts-ignore + authors.find((authors: string) => { + // @ts-ignore + return authors.author_id === authorArray[i] + }) + ) + } const { basePath } = useRouter() @@ -102,22 +113,17 @@ function BlogPostPage(props: any) { return (
    - - +
    +
    -

    {label}

    +

    {label}

    -

    {post.title}

    -

    {post.date}

    +

    {post.title}

    +

    {post.date}

    -
    - {post.tags.map((tag: string) => { - return {tag} - })} -
    - - +
    +
    ) @@ -191,7 +197,7 @@ function BlogPostPage(props: any) {

    Back @@ -203,44 +209,52 @@ function BlogPostPage(props: any) {