SQLEditor.queries.ts1603 lines · main
| 1 | import type { SQLTemplate } from './SQLEditor.types' |
| 2 | import { DOCS_URL } from '@/lib/constants' |
| 3 | |
| 4 | export const SQL_TEMPLATES: SQLTemplate[] = [ |
| 5 | { |
| 6 | id: 1, |
| 7 | type: 'template', |
| 8 | title: 'Create table', |
| 9 | description: 'Basic table template. Change "table_name" to the name you prefer.', |
| 10 | sql: `create table table_name ( |
| 11 | id bigint generated by default as identity primary key, |
| 12 | inserted_at timestamp with time zone default timezone('utc'::text, now()) not null, |
| 13 | updated_at timestamp with time zone default timezone('utc'::text, now()) not null, |
| 14 | data jsonb, |
| 15 | name text |
| 16 | );`, |
| 17 | }, |
| 18 | { |
| 19 | id: 2, |
| 20 | type: 'template', |
| 21 | title: 'Add view', |
| 22 | description: |
| 23 | 'Template to add a view. Make sure to change the table and column names to ones that already exist.', |
| 24 | sql: `CREATE VIEW countries_view AS |
| 25 | SELECT id, continent |
| 26 | FROM countries;`, |
| 27 | }, |
| 28 | { |
| 29 | id: 3, |
| 30 | type: 'template', |
| 31 | title: 'Add column', |
| 32 | description: 'Template to add a column. Make sure to change the name and type.', |
| 33 | sql: `alter table table_name |
| 34 | add column new_column_name data_type;`, |
| 35 | }, |
| 36 | { |
| 37 | id: 4, |
| 38 | type: 'template', |
| 39 | title: 'Add comments', |
| 40 | description: 'Templates to add a comment to either a table or a column.', |
| 41 | sql: `comment on table table_name is 'Table description'; |
| 42 | comment on column table_name.column_name is 'Column description';`, |
| 43 | }, |
| 44 | { |
| 45 | id: 5, |
| 46 | type: 'template', |
| 47 | title: 'Show extensions', |
| 48 | description: 'Get a list of extensions in your database and status.', |
| 49 | sql: `select |
| 50 | name, comment, default_version, installed_version |
| 51 | from |
| 52 | pg_available_extensions |
| 53 | order by |
| 54 | name asc;`, |
| 55 | }, |
| 56 | { |
| 57 | id: 6, |
| 58 | type: 'template', |
| 59 | title: 'Show version', |
| 60 | description: 'Get your Postgres version.', |
| 61 | sql: `select * from |
| 62 | (select version()) as version, |
| 63 | (select current_setting('server_version_num')) as version_number;`, |
| 64 | }, |
| 65 | { |
| 66 | id: 7, |
| 67 | type: 'template', |
| 68 | title: 'Show active connections', |
| 69 | description: 'Get the number of active and max connections.', |
| 70 | sql: `select * from |
| 71 | (select count(pid) as active_connections FROM pg_stat_activity where state = 'active') active_connections, |
| 72 | (select setting as max_connections from pg_settings where name = 'max_connections') max_connections;`, |
| 73 | }, |
| 74 | { |
| 75 | id: 8, |
| 76 | type: 'template', |
| 77 | title: 'Automatically update timestamps', |
| 78 | description: 'Update a column timestamp on every update.', |
| 79 | sql: ` |
| 80 | create extension if not exists moddatetime schema extensions; |
| 81 | |
| 82 | -- assuming the table name is "todos", and a timestamp column "updated_at" |
| 83 | -- this trigger will set the "updated_at" column to the current timestamp for every update |
| 84 | create trigger |
| 85 | handle_updated_at before update |
| 86 | on todos |
| 87 | for each row execute |
| 88 | procedure moddatetime(updated_at); |
| 89 | `.trim(), |
| 90 | }, |
| 91 | { |
| 92 | id: 9, |
| 93 | type: 'template', |
| 94 | title: 'Increment field value', |
| 95 | description: 'Update a field with incrementing value using a function.', |
| 96 | sql: ` |
| 97 | create function increment(row_id int) |
| 98 | returns void as |
| 99 | $$ |
| 100 | update table_name |
| 101 | set field_name = field_name + 1 |
| 102 | where id = row_id; |
| 103 | $$ |
| 104 | language sql volatile; |
| 105 | |
| 106 | -- you can call the function from your browser with briven-js |
| 107 | -- const { data, error } = await briven.rpc('increment', { row_id: 2 }) |
| 108 | `.trim(), |
| 109 | }, |
| 110 | { |
| 111 | id: 10, |
| 112 | type: 'template', |
| 113 | title: 'pg_stat_statements report', |
| 114 | description: 'Select from pg_stat_statements and view recent queries', |
| 115 | sql: `-- pg_stat_statements report |
| 116 | |
| 117 | -- A limit of 100 has been added below |
| 118 | |
| 119 | select |
| 120 | auth.rolname, |
| 121 | statements.query, |
| 122 | statements.calls, |
| 123 | -- -- Postgres 13, 14 |
| 124 | statements.total_exec_time + statements.total_plan_time as total_time, |
| 125 | statements.min_exec_time + statements.min_plan_time as min_time, |
| 126 | statements.max_exec_time + statements.max_plan_time as max_time, |
| 127 | statements.mean_exec_time + statements.mean_plan_time as mean_time, |
| 128 | -- -- Postgres <= 12 |
| 129 | -- total_time, |
| 130 | -- min_time, |
| 131 | -- max_time, |
| 132 | -- mean_time, |
| 133 | statements.rows / statements.calls as avg_rows, |
| 134 | statements.wal_bytes, |
| 135 | statements.wal_records |
| 136 | from pg_stat_statements as statements |
| 137 | inner join pg_authid as auth on statements.userid = auth.oid |
| 138 | order by |
| 139 | total_time desc |
| 140 | limit |
| 141 | 100;`, |
| 142 | }, |
| 143 | { |
| 144 | id: 11, |
| 145 | type: 'quickstart', |
| 146 | title: 'Colors', |
| 147 | description: 'Create a table with a list of colors and their hex values.', |
| 148 | sql: `-- Information from Wikipedia "List of Colors" |
| 149 | CREATE TYPE public.color_source AS ENUM ( |
| 150 | '99COLORS_NET', |
| 151 | 'ART_PAINTS_YG07S', |
| 152 | 'BYRNE', |
| 153 | 'CRAYOLA', |
| 154 | 'CMYK_COLOR_MODEL', |
| 155 | 'COLORCODE_IS', |
| 156 | 'COLORHEXA', |
| 157 | 'COLORXS', |
| 158 | 'CORNELL_UNIVERSITY', |
| 159 | 'COLUMBIA_UNIVERSITY', |
| 160 | 'DUKE_UNIVERSITY', |
| 161 | 'ENCYCOLORPEDIA_COM', |
| 162 | 'ETON_COLLEGE', |
| 163 | 'FANTETTI_AND_PETRACCHI', |
| 164 | 'FINDTHEDATA_COM', |
| 165 | 'FERRARIO_1919', |
| 166 | 'FEDERAL_STANDARD_595', |
| 167 | 'FLAG_OF_INDIA', |
| 168 | 'FLAG_OF_SOUTH_AFRICA', |
| 169 | 'GLAZEBROOK_AND_BALDRY', |
| 170 | 'GOOGLE', |
| 171 | 'HEXCOLOR_CO', |
| 172 | 'ISCC_NBS', |
| 173 | 'KELLY_MOORE', |
| 174 | 'MATTEL', |
| 175 | 'MAERZ_AND_PAUL', |
| 176 | 'MILK_PAINT', |
| 177 | 'MUNSELL_COLOR_WHEEL', |
| 178 | 'NATURAL_COLOR_SYSTEM', |
| 179 | 'PANTONE', |
| 180 | 'PLOCHERE', |
| 181 | 'POURPRE_COM', |
| 182 | 'RAL', |
| 183 | 'RESENE', |
| 184 | 'RGB_COLOR_MODEL', |
| 185 | 'THOM_POOLE', |
| 186 | 'UNIVERSITY_OF_ALABAMA', |
| 187 | 'UNIVERSITY_OF_CALIFORNIA_DAVIS', |
| 188 | 'UNIVERSITY_OF_CAMBRIDGE', |
| 189 | 'UNIVERSITY_OF_NORTH_CAROLINA', |
| 190 | 'UNIVERSITY_OF_TEXAS_AT_AUSTIN', |
| 191 | 'X11_WEB', |
| 192 | 'XONA_COM' |
| 193 | ); |
| 194 | |
| 195 | create table public.colors ( |
| 196 | id bigint generated by default as identity primary key, |
| 197 | name text, |
| 198 | hex text not null, |
| 199 | red int2, |
| 200 | green int2, |
| 201 | blue int2, |
| 202 | hue int2, |
| 203 | sat_hsl int2, |
| 204 | light_hsl int2, |
| 205 | sat_hsv int2, |
| 206 | val_hsv int2, |
| 207 | source color_source |
| 208 | ); |
| 209 | |
| 210 | comment on table colors is 'Full list of colors (based on various sources)'; |
| 211 | comment on column colors.name is 'Name of the color'; |
| 212 | comment on column colors.hex is 'Hex tripliets of the color for HTML web colors'; |
| 213 | comment on column colors.red is 'Red in RGB (%)'; |
| 214 | comment on column colors.green is 'Green in RGB (%)'; |
| 215 | comment on column colors.blue is 'Blue in RGB (%)'; |
| 216 | comment on column colors.hue is 'Hue in HSL (°)'; |
| 217 | comment on column colors.sat_hsl is 'Saturation in HSL (%)'; |
| 218 | comment on column colors.light_hsl is 'Light in HSL (%)'; |
| 219 | comment on column colors.sat_hsv is 'Saturation in HSV (%)'; |
| 220 | comment on column colors.val_hsv is 'Value in HSV (%)'; |
| 221 | comment on column colors.source is 'Source of information on the color'; |
| 222 | |
| 223 | insert into public.colors (name, hex, red, green, blue, hue, sat_hsl, light_hsl, sat_hsv, val_hsv, source) values |
| 224 | ('Absolute Zero', '#0048BA', 0, 28, 73, 217, 100, 37, 100, 73, 'CRAYOLA'), |
| 225 | ('Acid green', '#B0BF1A', 69, 75, 10, 65, 76, 43, 76, 75, 'ART_PAINTS_YG07S'), |
| 226 | ('Aero', '#7CB9E8', 49, 73, 91, 206, 70, 70, 47, 91, 'MAERZ_AND_PAUL'), |
| 227 | ('African violet', '#B284BE', 70, 52, 75, 288, 31, 63, 31.5, '75', 'PANTONE'), |
| 228 | ('Air superiority blue', '#72A0C1', 45, 63, 76, 205, 39, 60, 41, 76, 'FEDERAL_STANDARD_595'), |
| 229 | ('Alice blue', '#F0F8FF', 94, 97, 100, 208, 100, 97, 6, 100, 'X11_WEB'), |
| 230 | ('Alizarin', '#DB2D43', 86, 18, 26, 352, 71, 52, 79, 86, 'MAERZ_AND_PAUL'), |
| 231 | ('Alloy orange', '#C46210', 77, 38, 6, 27, 85, 42, 92, 77, 'CRAYOLA'), |
| 232 | ('Almond', '#EED9C4', 93, 85, 77, 30, 55, 85, 18, 93, 'CRAYOLA'), |
| 233 | ('Amaranth deep purple', '#9F2B68', 62, 17, 41, 328, 57, 40, 73, 62, 'MAERZ_AND_PAUL'), |
| 234 | ('Amaranth pink', '#F19CBB', 95, 61, 73, 338, 75, 78, 35, 95, 'MAERZ_AND_PAUL'), |
| 235 | ('Amaranth purple', '#AB274F', 67, 15, 31, 342, 63, 41, 77, 67, 'MAERZ_AND_PAUL'), |
| 236 | ('Amazon', '#3B7A57', 23, 48, 34, 147, 35, 36, 52, 48, 'XONA_COM'), |
| 237 | ('Amber', '#FFBF00', 100, 75, 0, 45, 100, 50, 100, 100, 'RGB_COLOR_MODEL'), |
| 238 | ('Amethyst', '#9966CC', 60, 40, 80, 270, 50, 60, 50, 80, 'X11_WEB'), |
| 239 | ('Android green', '#3DDC84', 24, 86, 53, 148, 69, 55, 72, 86, 'GOOGLE'), |
| 240 | ('Antique brass', '#C88A65', 78, 54, 40, 22, 47, 59, 49, 78, 'CRAYOLA'), |
| 241 | ('Antique bronze', '#665D1E', 40, 36, 12, 53, 55, 26, 71, 40, 'ISCC_NBS'), |
| 242 | ('Antique fuchsia', '#915C83', 57, 36, 51, 316, 22, 46, 37, 57, 'PLOCHERE'), |
| 243 | ('Antique ruby', '#841B2D', 52, 11, 18, 350, 66, 31, 80, 52, 'ISCC_NBS'), |
| 244 | ('Antique white', '#FAEBD7', 98, 92, 84, 34, 78, 91, 14, 98, 'X11_WEB'), |
| 245 | ('Apricot', '#FBCEB1', 98, 81, 69, 24, 90, 84, 29, 98, 'MAERZ_AND_PAUL'), |
| 246 | ('Aqua', '#00FFFF', 0, 100, 100, 180, 100, 50, 100, 100, 'X11_WEB'), |
| 247 | ('Aquamarine', '#7FFFD4', 50, 100, 83, 160, 100, 75, 50, 100, 'X11_WEB'), |
| 248 | ('Arctic lime', '#D0FF14', 82, 100, 8, 72, 100, 54, 92, 100, 'CRAYOLA'), |
| 249 | ('Artichoke green', '#4B6F44', 29, 44, 27, 110, 24, 35, 39, 44, 'PANTONE'), |
| 250 | ('Arylide yellow', '#E9D66B', 91, 84, 42, 51, 74, 67, 54, 91, 'COLORHEXA'), |
| 251 | ('Ash gray', '#B2BEB5', 70, 75, 71, 135, 9, 72, 6, 75, 'ISCC_NBS'), |
| 252 | ('Atomic tangerine', '#FF9966', 100, 60, 40, 20, 100, 70, 60, 100, 'CRAYOLA'), |
| 253 | ('Aureolin', '#FDEE00', 99, 93, 0, 56, 100, 50, 100, 99, 'X11_WEB'), |
| 254 | ('Azure', '#007FFF', 0, 50, 100, 210, 100, 50, 100, 100, 'RGB_COLOR_MODEL'), |
| 255 | ('Azure (X11/web color)', '#F0FFFF', 94, 100, 100, 180, 100, 97, 6, 100, 'X11_WEB'), |
| 256 | ('Baby blue', '#89CFF0', 54, 81, 94, 199, 77, 74, 43, 94, 'MAERZ_AND_PAUL'), |
| 257 | ('Baby blue eyes', '#A1CAF1', 63, 79, 95, 209, 74, 79, 33, 95, 'PLOCHERE'), |
| 258 | ('Baby pink', '#F4C2C2', 96, 76, 76, 0, 69, 86, 20, 96, 'ISCC_NBS'), |
| 259 | ('Baby powder', '#FEFEFA', 100, 100, 98, 60, 67, 99, 2, 100, 'CRAYOLA'), |
| 260 | ('Baker-Miller pink', '#FF91AF', 100, 57, 69, 344, 100, 78, 43, 100, 'BYRNE'), |
| 261 | ('Banana Mania', '#FAE7B5', 98, 91, 71, 43, 87, 85, 28, 98, 'CRAYOLA'), |
| 262 | ('Barbie Pink', '#DA1884', 85, 9, 52, 327, 80, 48, 89, 85, 'MATTEL'), |
| 263 | ('Barn red', '#7C0A02', 49, 4, 1, 4, 97, 25, 98, 49, 'MILK_PAINT'), |
| 264 | ('Battleship grey', '#848482', 52, 52, 51, 60, 1, 51, 2, 52, 'ISCC_NBS'), |
| 265 | ('Beau blue', '#BCD4E6', 74, 83, 90, 206, 46, 82, 18, 90, 'PLOCHERE'), |
| 266 | ('Beaver', '#9F8170', 62, 51, 44, 22, 20, 53, 30, 62, 'CRAYOLA'), |
| 267 | ('Beige', '#F5F5DC', 96, 96, 86, 60, 56, 91, 10, 96, 'X11_WEB'), |
| 268 | ('B''dazzled blue', '#2E5894', 18, 35, 58, 215, 53, 38, 69, 58, 'CRAYOLA'), |
| 269 | ('Big dip o''ruby', '#9C2542', 61, 15, 26, 345, 62, 38, 76, 61, 'CRAYOLA'), |
| 270 | ('Bisque', '#FFE4C4', 100, 89, 77, 33, 100, 88, 23, 100, 'X11_WEB'), |
| 271 | ('Bistre', '#3D2B1F', 24, 17, 12, 24, 33, 18, 49, 24, '99COLORS_NET'), |
| 272 | ('Bistre brown', '#967117', 59, 44, 9, 43, 73, 34, 85, 59, 'ISCC_NBS'), |
| 273 | ('Bitter lemon', '#CAE00D', 79, 88, 5, 66, 89, 47, 94, 88, 'XONA_COM'), |
| 274 | ('Black', '#000000', 0, 0, 0, 0, 0, 0, 0, 0, 'RGB_COLOR_MODEL'), |
| 275 | ('Black bean', '#3D0C02', 24, 5, 1, 10, 94, 12, 97, 24, 'XONA_COM'), |
| 276 | ('Black coral', '#54626F', 33, 38, 44, 209, 14, 38, 24, 44, 'CRAYOLA'), |
| 277 | ('Black olive', '#3B3C36', 23, 24, 21, 70, 5, 22, 10, 24, 'RAL'), |
| 278 | ('Black Shadows', '#BFAFB2', 75, 69, 70, 349, 11, 72, 8, 75, 'CRAYOLA'), |
| 279 | ('Blanched almond', '#FFEBCD', 100, 92, 80, 36, 100, 90, 20, 100, 'X11_WEB'), |
| 280 | ('Blast-off bronze', '#A57164', 65, 44, 39, 12, 27, 52, 39, 65, 'CRAYOLA'), |
| 281 | ('Bleu de France', '#318CE7', 19, 55, 91, 210, 79, 55, 79, 91, 'POURPRE_COM'), |
| 282 | ('Blizzard blue', '#ACE5EE', 67, 90, 93, 188, 66, 80, 28, 93, 'CRAYOLA'), |
| 283 | ('Blood red', '#660000', 40, 0, 0, 0, 100, 20, 100, 40, 'THOM_POOLE'), |
| 284 | ('Blue', '#0000FF', 0, 0, 100, 240, 100, 50, 100, 100, 'X11_WEB'), |
| 285 | ('Blue (Crayola)', '#1F75FE', 12, 46, 100, 217, 99, 56, 88, 100, 'CRAYOLA'), |
| 286 | ('Blue (Munsell)', '#0093AF', 0, 58, 69, 190, 100, 34, 100, 69, 'MUNSELL_COLOR_WHEEL'), |
| 287 | ('Blue (NCS)', '#0087BD', 0, 53, 74, 197, 100, 37, 100, 74, 'NATURAL_COLOR_SYSTEM'), |
| 288 | ('Blue (Pantone)', '#0018A8', 0, 9, 66, 231, 100, 33, 100, 66, 'PANTONE'), |
| 289 | ('Blue (pigment)', '#333399', 20, 20, 60, 240, 50, 40, 67, 60, 'CMYK_COLOR_MODEL'), |
| 290 | ('Blue bell', '#A2A2D0', 64, 64, 82, 240, 33, 73, 22, 82, 'CRAYOLA'), |
| 291 | ('Blue-gray (Crayola)', '#6699CC', 40, 60, 80, 210, 50, 60, 50, 80, 'CRAYOLA'), |
| 292 | ('Blue jeans', '#5DADEC', 36, 68, 93, 206, 79, 65, 61, 93, 'CRAYOLA'), |
| 293 | ('Blue sapphire', '#126180', 7, 38, 50, 197, 75, 29, 86, 50, 'PANTONE'), |
| 294 | ('Blue-violet', '#8A2BE2', 54, 17, 89, 271, 76, 53, 81, 89, 'X11_WEB'), |
| 295 | ('Blue yonder', '#5072A7', 31, 45, 65, 217, 35, 48, 52, 65, 'PANTONE'), |
| 296 | ('Bluetiful', '#3C69E7', 24, 41, 91, 224, 78, 57, 74, 91, 'CRAYOLA'), |
| 297 | ('Blush', '#DE5D83', 87, 36, 51, 342, 66, 62, 58, 87, 'CRAYOLA'), |
| 298 | ('Bole', '#79443B', 47, 27, 23, 9, 34, 35, 51, 47, 'ISCC_NBS'), |
| 299 | ('Bone', '#E3DAC9', 89, 85, 79, 39, 32, 84, 11, 89, 'KELLY_MOORE'), |
| 300 | ('Brick red', '#CB4154', 80, 25, 33, 352, 57, 53, 68, 80, 'CRAYOLA'), |
| 301 | ('Bright lilac', '#D891EF', 85, 57, 94, 285, 75, 75, 39, 94, 'CRAYOLA'), |
| 302 | ('Bright yellow (Crayola)', '#FFAA1D', 100, 67, 11, 37, 100, 56, 89, 100, 'CRAYOLA'), |
| 303 | ('British racing green', '#004225', 0, 26, 15, 154, 100, 13, 100, 26, 'COLORHEXA'), |
| 304 | ('Bronze', '#CD7F32', 80, 50, 20, 30, 61, 50, 76, 80, 'MAERZ_AND_PAUL'), |
| 305 | ('Brown', '#964B00', 59, 29, 0, 30, 100, 29, 100, 59, 'COLORXS'), |
| 306 | ('Brown sugar', '#AF6E4D', 69, 43, 30, 20, 39, 49, 56, 69, 'CRAYOLA'), |
| 307 | ('Bud green', '#7BB661', 48, 71, 38, 102, 37, 55, 47, 71, 'PANTONE'), |
| 308 | ('Buff', '#FFC680', 100, 78, 50, 33, 100, 75, 50, 100, 'MAERZ_AND_PAUL'), |
| 309 | ('Burgundy', '#800020', 50, 0, 13, 345, 100, 25, 100, 50, 'MAERZ_AND_PAUL'), |
| 310 | ('Burlywood', '#DEB887', 87, 72, 53, 34, 57, 70, 39, 87, 'X11_WEB'), |
| 311 | ('Burnished brown', '#A17A74', 63, 48, 45, 8, 19, 54, 28, 63, 'CRAYOLA'), |
| 312 | ('Burnt orange', '#CC5500', 80, 33, 0, 25, 100, 40, 100, 80, 'UNIVERSITY_OF_TEXAS_AT_AUSTIN'), |
| 313 | ('Burnt sienna', '#E97451', 91, 45, 32, 14, 78, 62, 65, 91, 'FERRARIO_1919'), |
| 314 | ('Burnt umber', '#8A3324', 54, 20, 14, 9, 59, 34, 74, 54, 'XONA_COM'), |
| 315 | ('Byzantine', '#BD33A4', 74, 20, 64, 311, 58, 47, 73, 74, 'MAERZ_AND_PAUL'), |
| 316 | ('Byzantium', '#702963', 44, 16, 39, 311, 46, 30, 63, 44, 'ISCC_NBS'), |
| 317 | ('Cadet blue', '#5F9EA0', 37, 62, 63, 182, 26, 50, 41, 63, 'X11_WEB'), |
| 318 | ('Cadet grey', '#91A3B0', 57, 64, 69, 205, 16, 63, 18, 69, 'ISCC_NBS'), |
| 319 | ('Cadmium green', '#006B3C', 0, 42, 24, 154, 100, 21, 100, 42, 'ISCC_NBS'), |
| 320 | ('Cadmium orange', '#ED872D', 93, 53, 18, 28, 84, 55, 81, 93, 'ISCC_NBS'), |
| 321 | ('Café au lait', '#A67B5B', 65, 48, 36, 26, 30, 50, 45, 65, 'ISCC_NBS'), |
| 322 | ('Café noir', '#4B3621', 29, 21, 13, 30, 39, 21, 56, 29, 'ISCC_NBS'), |
| 323 | ('Cambridge blue', '#A3C1AD', 64, 76, 68, 140, 20, 70, 16, 76, 'UNIVERSITY_OF_CAMBRIDGE'), |
| 324 | ('Camel', '#C19A6B', 76, 60, 42, 33, 41, 59, 45, 76, 'ISCC_NBS'), |
| 325 | ('Cameo pink', '#EFBBCC', 94, 73, 80, 340, 62, 84, 22, 94, 'ISCC_NBS'), |
| 326 | ('Canary', '#FFFF99', 100, 100, 60, 60, 100, 80, 40, 100, 'CRAYOLA'), |
| 327 | ('Canary yellow', '#FFEF00', 100, 94, 0, 56, 100, 50, 100, 100, 'CMYK_COLOR_MODEL'), |
| 328 | ('Candy pink', '#E4717A', 89, 44, 48, 355, 68, 67, 50, 89, 'ISCC_NBS'), |
| 329 | ('Cardinal', '#C41E3A', 77, 12, 23, 350, 74, 44, 85, 77, 'MAERZ_AND_PAUL'), |
| 330 | ('Caribbean green', '#00CC99', 0, 80, 60, 165, 100, 40, 100, 80, 'CRAYOLA'), |
| 331 | ('Carmine', '#960018', 59, 0, 9, 350, 100, 29, 100, 59, 'POURPRE_COM'), |
| 332 | ('Carmine (M&P)', '#D70040', 84, 0, 25, 342, 100, 42, 100, 84, 'MAERZ_AND_PAUL'), |
| 333 | ('Carnation pink', '#FFA6C9', 100, 65, 79, 336, 100, 83, 35, 100, 'CRAYOLA'), |
| 334 | ('Carnelian', '#B31B1B', 70, 11, 11, 0, 74, 40, 85, 70, 'CORNELL_UNIVERSITY'), |
| 335 | ('Carolina blue', '#56A0D3', 34, 63, 83, 204, 59, 58, 59, 83, 'UNIVERSITY_OF_NORTH_CAROLINA'), |
| 336 | ('Carrot orange', '#ED9121', 93, 57, 13, 33, 85, 53, 86, 93, 'MAERZ_AND_PAUL'), |
| 337 | ('Catawba', '#703642', 44, 21, 26, 348, 35, 33, 52, 44, 'MAERZ_AND_PAUL'), |
| 338 | ('Cedar Chest', '#C95A49', 79, 35, 29, 8, 54, 54, 64, 79, 'CRAYOLA'), |
| 339 | ('Celadon', '#ACE1AF', 67, 88, 69, 123, 47, 78, 24, 88, 'ENCYCOLORPEDIA_COM'), |
| 340 | ('Celeste', '#B2FFFF', 70, 100, 100, 180, 100, 85, 30, 100, 'FANTETTI_AND_PETRACCHI'), |
| 341 | ('Cerise', '#DE3163', 87, 19, 39, 343, 72, 53, 78, 87, 'MAERZ_AND_PAUL'), |
| 342 | ('Cerulean', '#007BA7', 0, 48, 65, 196, 100, 33, 100, 65, 'MAERZ_AND_PAUL'), |
| 343 | ('Cerulean blue', '#2A52BE', 16, 32, 75, 224, 64, 46, 78, 75, 'MAERZ_AND_PAUL'), |
| 344 | ('Cerulean frost', '#6D9BC3', 43, 61, 76, 208, 42, 60, 44, 76, 'CRAYOLA'), |
| 345 | ('Cerulean (Crayola)', '#1DACD6', 11, 67, 84, 194, 76, 48, 86, 84, 'CRAYOLA'), |
| 346 | ('Cerulean (RGB)', '#0040FF', 0, 25, 100, 225, 100, 50, 100, 100, null), |
| 347 | ('Champagne', '#F7E7CE', 97, 91, 81, 37, 72, 89, 17, 97, 'MAERZ_AND_PAUL'), |
| 348 | ('Champagne pink', '#F1DDCF', 95, 87, 81, 25, 55, 88, 14, 95, 'PANTONE'), |
| 349 | ('Charcoal', '#36454F', 21, 27, 31, 204, 19, 26, 32, 31, 'ISCC_NBS'), |
| 350 | ('Charm pink', '#E68FAC', 90, 56, 67, 340, 64, 73, 38, 90, 'PLOCHERE'), |
| 351 | ('Chartreuse (web)', '#80FF00', 50, 100, 0, 90, 100, 50, 100, 100, 'RGB_COLOR_MODEL'), |
| 352 | ('Cherry blossom pink', '#FFB7C5', 100, 72, 77, 348, 100, 86, 28, 100, 'MAERZ_AND_PAUL'), |
| 353 | ('Chestnut', '#954535', 58, 27, 21, 10, 48, 40, 64, 58, 'MAERZ_AND_PAUL'), |
| 354 | ('Chili red', '#E23D28', 89, 24, 16, 5, 76, 52, 183, 125, 'FLAG_OF_SOUTH_AFRICA'), |
| 355 | ('China pink', '#DE6FA1', 87, 44, 63, 333, 63, 65, 50, 87, 'PLOCHERE'), |
| 356 | ('Chinese red', '#AA381E', 67, 22, 12, 11, 70, 39, 82, 67, 'ISCC_NBS'), |
| 357 | ('Chinese violet', '#856088', 52, 38, 53, 296, 17, 46, 29, 53, 'PANTONE'), |
| 358 | ('Chinese yellow', '#FFB200', 100, 70, 0, 42, 100, 50, 100, 100, 'ISCC_NBS'), |
| 359 | ('Chocolate (traditional)', '#7B3F00', 48, 25, 0, 31, 100, 24, 100, 48, 'MAERZ_AND_PAUL'), |
| 360 | ('Chocolate (web)', '#D2691E', 82, 41, 12, 25, 75, 47, 86, 82, 'X11_WEB'), |
| 361 | ('Cinereous', '#98817B', 60, 51, 48, 12, 12, 54, 19, 60, 'MAERZ_AND_PAUL'), |
| 362 | ('Cinnabar', '#E34234', 89, 26, 20, 5, 76, 55, 77, 89, 'MAERZ_AND_PAUL'), |
| 363 | ('Cinnamon Satin', '#CD607E', 80, 38, 49, 343, 52, 59, 53, 80, 'CRAYOLA'), |
| 364 | ('Citrine', '#E4D00A', 89, 82, 4, 54, 92, 47, 96, 89, 'MAERZ_AND_PAUL'), |
| 365 | ('Citron', '#9FA91F', 62, 66, 12, 64, 69, 39, 82, 66, 'XONA_COM'), |
| 366 | ('Claret', '#7F1734', 50, 9, 20, 343, 69, 29, 82, 50, 'XONA_COM'), |
| 367 | ('Coffee', '#6F4E37', 44, 31, 22, 25, 34, 33, 50, 44, 'ISCC_NBS'), |
| 368 | ('Columbia Blue', '#B9D9EB', 73, 85, 92, 202, 56, 82, 21, 92, 'COLUMBIA_UNIVERSITY'), |
| 369 | ('Congo pink', '#F88379', 97, 51, 47, 5, 90, 72, 51, 97, 'ISCC_NBS'), |
| 370 | ('Cool grey', '#8C92AC', 55, 57, 67, 229, 16, 61, 19, 67, 'ISCC_NBS'), |
| 371 | ('Copper', '#B87333', 72, 45, 20, 29, 57, 46, 72, 72, 'MAERZ_AND_PAUL'), |
| 372 | ('Copper (Crayola)', '#DA8A67', 85, 54, 40, 18, 61, 63, 53, 85, 'CRAYOLA'), |
| 373 | ('Copper penny', '#AD6F69', 68, 44, 41, 5, 29, 55, 39, 68, 'CRAYOLA'), |
| 374 | ('Copper red', '#CB6D51', 80, 43, 32, 14, 54, 56, 60, 80, 'ISCC_NBS'), |
| 375 | ('Copper rose', '#996666', 60, 40, 40, 0, 20, 50, 33, 60, '99COLORS_NET'), |
| 376 | ('Coquelicot', '#FF3800', 100, 22, 0, 13, 100, 50, 100, 100, 'COLORHEXA'), |
| 377 | ('Coral', '#FF7F50', 100, 50, 31, 16, 100, 66, 69, 100, 'X11_WEB'), |
| 378 | ('Coral pink', '#F88379', 97, 51, 47, 5, 90, 72, 51, 97, 'ISCC_NBS'), |
| 379 | ('Cordovan', '#893F45', 54, 25, 27, 355, 37, 39, 54, 54, 'PANTONE'), |
| 380 | ('Corn', '#FBEC5D', 98, 93, 36, 54, 95, 68, 63, 98, 'MAERZ_AND_PAUL'), |
| 381 | ('Cornflower blue', '#6495ED', 39, 58, 93, 219, 79, 66, 58, 93, 'X11_WEB'), |
| 382 | ('Cornsilk', '#FFF8DC', 100, 97, 86, 48, 100, 93, 14, 100, 'X11_WEB'), |
| 383 | ('Cosmic cobalt', '#2E2D88', 18, 18, 53, 241, 50, 36, 67, 53, 'CRAYOLA'), |
| 384 | ('Cosmic latte', '#FFF8E7', 100, 97, 91, 43, 100, 95, 9, 100, 'GLAZEBROOK_AND_BALDRY'), |
| 385 | ('Coyote brown', '#81613C', 51, 38, 24, 32, 37, 37, 52, 51, 'COLORCODE_IS'), |
| 386 | ('Cotton candy', '#FFBCD9', 100, 74, 85, 334, 100, 87, 26, 100, 'CRAYOLA'), |
| 387 | ('Cream', '#FFFDD0', 100, 99, 82, 57, 100, 91, 18, 100, 'MAERZ_AND_PAUL'), |
| 388 | ('Crimson', '#DC143C', 86, 8, 24, 348, 83, 47, 91, 86, 'X11_WEB'), |
| 389 | ('Crimson (UA)', '#9E1B32', 62, 11, 20, 349, 71, 36, 83, 62, 'UNIVERSITY_OF_ALABAMA'), |
| 390 | ('Cultured Pearl', '#F5F5F5', 96, 96, 96, 0, 0, 96, 0, 96, 'CRAYOLA'), |
| 391 | ('Cyan', '#00FFFF', 0, 100, 100, 180, 100, 50, 100, 100, 'X11_WEB'), |
| 392 | ('Cyan (process)', '#00B7EB', 0, 72, 92, 193, 100, 46, 100, 92, 'CMYK_COLOR_MODEL'), |
| 393 | ('Cyber grape', '#58427C', 35, 26, 49, 263, 31, 37, 47, 49, 'CRAYOLA'), |
| 394 | ('Cyber yellow', '#FFD300', 100, 83, 0, 50, 100, 50, 100, 100, 'PANTONE'), |
| 395 | ('Cyclamen', '#F56FA1', 96, 44, 63, 338, 87, 70, 54, 96, 'CRAYOLA'), |
| 396 | ('Dandelion', '#FED85D', 100, 85, 36, 46, 99, 68, 63, 100, 'CRAYOLA'), |
| 397 | ('Dark brown', '#654321', 40, 26, 13, 30, 51, 26, 67, 40, 'X11_WEB'), |
| 398 | ('Dark byzantium', '#5D3954', 36, 22, 33, 315, 24, 29, 39, 36, 'ISCC_NBS'), |
| 399 | ('Dark cyan', '#008B8B', 0, 55, 55, 180, 100, 27, 100, 55, 'X11_WEB'), |
| 400 | ('Dark electric blue', '#536878', 33, 41, 47, 206, 18, 40, 31, 47, 'ISCC_NBS'), |
| 401 | ('Dark goldenrod', '#B8860B', 72, 53, 4, 43, 89, 38, 94, 72, 'X11_WEB'), |
| 402 | ('Dark green (X11)', '#006400', 0, 39, 0, 120, 100, 20, 100, 39, 'X11_WEB'), |
| 403 | ('Dark jungle green', '#1A2421', 10, 14, 13, 162, 16, 12, 28, 14, 'ISCC_NBS'), |
| 404 | ('Dark khaki', '#BDB76B', 74, 72, 42, 56, 38, 58, 43, 74, 'X11_WEB'), |
| 405 | ('Dark lava', '#483C32', 28, 24, 20, 27, 18, 24, 31, 28, 'ISCC_NBS'), |
| 406 | ('Dark liver (horses)', '#543D37', 33, 24, 22, 12, 21, 27, 35, 33, 'UNIVERSITY_OF_CALIFORNIA_DAVIS'), |
| 407 | ('Dark magenta', '#8B008B', 55, 0, 55, 300, 100, 27, 100, 55, 'X11_WEB'), |
| 408 | ('Dark olive green', '#556B2F', 33, 42, 18, 82, 39, 30, 56, 42, 'X11_WEB'), |
| 409 | ('Dark orange', '#FF8C00', 100, 55, 0, 33, 100, 50, 100, 100, 'X11_WEB'), |
| 410 | ('Dark orchid', '#9932CC', 60, 20, 80, 280, 61, 50, 75, 80, 'X11_WEB'), |
| 411 | ('Dark purple', '#301934', 19, 10, 20, 291, 35, 15, 51, 20, 'ISCC_NBS'), |
| 412 | ('Dark red', '#8B0000', 55, 0, 0, 0, 100, 27, 100, 55, 'X11_WEB'), |
| 413 | ('Dark salmon', '#E9967A', 91, 59, 48, 15, 72, 70, 48, 91, 'X11_WEB'), |
| 414 | ('Dark sea green', '#8FBC8F', 56, 74, 56, 120, 25, 65, 24, 74, 'X11_WEB'), |
| 415 | ('Dark sienna', '#3C1414', 24, 8, 8, 0, 50, 16, 67, 24, 'ISCC_NBS'), |
| 416 | ('Dark sky blue', '#8CBED6', 55, 75, 84, 199, 47, 69, 35, 84, 'PANTONE'), |
| 417 | ('Dark slate blue', '#483D8B', 28, 24, 55, 248, 39, 39, 56, 55, 'X11_WEB'), |
| 418 | ('Dark slate gray', '#2F4F4F', 18, 31, 31, 180, 25, 25, 41, 31, 'X11_WEB'), |
| 419 | ('Dark spring green', '#177245', 9, 45, 27, 150, 66, 27, 80, 45, 'X11_WEB'), |
| 420 | ('Dark turquoise', '#00CED1', 0, 81, 82, 181, 100, 41, 100, 82, 'X11_WEB'), |
| 421 | ('Dark violet', '#9400D3', 58, 0, 83, 282, 100, 41, 100, 83, 'X11_WEB'), |
| 422 | ('Davy''s grey', '#555555', 33, 33, 33, 0, 0, 33, 0, 33, 'ISCC_NBS'), |
| 423 | ('Deep cerise', '#DA3287', 85, 20, 53, 330, 69, 53, 77, 85, 'CRAYOLA'), |
| 424 | ('Deep champagne', '#FAD6A5', 98, 84, 65, 35, 90, 81, 34, 98, 'ISCC_NBS'), |
| 425 | ('Deep chestnut', '#B94E48', 73, 31, 28, 3, 45, 50, 61, 73, 'CRAYOLA'), |
| 426 | ('Deep jungle green', '#004B49', 0, 29, 29, 178, 100, 15, 100, 29, 'ISCC_NBS'), |
| 427 | ('Deep pink', '#FF1493', 100, 8, 58, 328, 100, 54, 92, 100, 'X11_WEB'), |
| 428 | ('Deep saffron', '#FF9933', 100, 60, 20, 30, 100, 60, 80, 100, 'FLAG_OF_INDIA'), |
| 429 | ('Deep sky blue', '#00BFFF', 0, 75, 100, 195, 100, 50, 100, 100, 'X11_WEB'), |
| 430 | ('Deep Space Sparkle', '#4A646C', 29, 39, 42, 194, 19, 36, 31, 42, 'CRAYOLA'), |
| 431 | ('Deep taupe', '#7E5E60', 49, 37, 38, 356, 15, 43, 25, 49, 'PANTONE'), |
| 432 | ('Denim', '#1560BD', 8, 38, 74, 213, 80, 41, 89, 74, 'CRAYOLA'), |
| 433 | ('Denim blue', '#2243B6', 13, 26, 71, 227, 69, 42, 81, 71, 'CRAYOLA'), |
| 434 | ('Desert', '#C19A6B', 76, 60, 42, 33, 41, 59, 45, 76, 'ISCC_NBS'), |
| 435 | ('Desert sand', '#EDC9AF', 93, 79, 69, 25, 63, 81, 26, 93, 'CRAYOLA'), |
| 436 | ('Dim gray', '#696969', 41, 41, 41, 0, 0, 41, 0, 41, 'X11_WEB'), |
| 437 | ('Dodger blue', '#1E90FF', 12, 56, 100, 210, 100, 56, 88, 100, 'X11_WEB'), |
| 438 | ('Drab dark brown', '#4A412A', 29, 25, 16, 43, 28, 23, 43, 29, 'PANTONE'), |
| 439 | ('Duke blue', '#00009C', 0, 0, 61, 240, 100, 31, 100, 61, 'DUKE_UNIVERSITY'), |
| 440 | ('Dutch white', '#EFDFBB', 94, 87, 73, 42, 62, 84, 22, 94, 'RESENE'), |
| 441 | ('Ebony', '#555D50', 33, 36, 31, 97, 8, 34, 14, 36, 'MAERZ_AND_PAUL'), |
| 442 | ('Ecru', '#C2B280', 76, 70, 50, 45, 35, 63, 34, 76, 'ISCC_NBS'), |
| 443 | ('Eerie black', '#1B1B1B', 11, 11, 11, 0, 0, 11, 0, 11, 'CRAYOLA'), |
| 444 | ('Eggplant', '#614051', 38, 25, 32, 329, 21, 32, 34, 38, 'CRAYOLA'), |
| 445 | ('Eggshell', '#F0EAD6', 94, 92, 84, 46, 46, 89, 11, 94, 'ISCC_NBS'), |
| 446 | ('Electric lime', '#CCFF00', 80, 100, 0, 72, 100, 50, 100, 100, 'CRAYOLA'), |
| 447 | ('Electric purple', '#BF00FF', 75, 0, 100, 285, 100, 50, 100, 100, 'X11_WEB'), |
| 448 | ('Electric violet', '#8F00FF', 56, 0, 100, 274, 100, 50, 100, 100, 'ISCC_NBS'), |
| 449 | ('Emerald', '#50C878', 31, 78, 47, 140, 52, 55, 60, 78, 'MAERZ_AND_PAUL'), |
| 450 | ('Eminence', '#6C3082', 42, 19, 51, 284, 46, 35, 63, 51, 'XONA_COM'), |
| 451 | ('English lavender', '#B48395', 71, 51, 58, 338, 25, 61, 27, 71, 'PANTONE'), |
| 452 | ('English red', '#AB4B52', 67, 29, 32, 356, 39, 48, 56, 67, 'ISCC_NBS'), |
| 453 | ('English vermillion', '#CC474B', 80, 28, 29, 358, 57, 54, 65, 80, 'CRAYOLA'), |
| 454 | ('English violet', '#563C5C', 34, 24, 36, 289, 21, 30, 35, 36, 'ISCC_NBS'), |
| 455 | ('Erin', '#00FF40', 0, 100, 25, 135, 100, 50, 100, 100, 'MAERZ_AND_PAUL'), |
| 456 | ('Eton blue', '#96C8A2', 59, 78, 64, 134, 31, 69, 25, 78, 'ETON_COLLEGE'), |
| 457 | ('Fallow', '#C19A6B', 76, 60, 42, 33, 41, 59, 45, 76, 'ISCC_NBS'), |
| 458 | ('Falu red', '#801818', 50, 9, 9, 0, 68, 30, 81, 50, 'COLORHEXA'), |
| 459 | ('Fandango', '#B53389', 71, 20, 54, 320, 56, 46, 72, 71, 'MAERZ_AND_PAUL'), |
| 460 | ('Fandango pink', '#DE5285', 87, 32, 52, 338, 68, 60, 63, 87, 'PANTONE'), |
| 461 | ('Fawn', '#E5AA70', 90, 67, 44, 30, 69, 67, 51, 90, 'X11_WEB'), |
| 462 | ('Fern green', '#4F7942', 31, 47, 26, 106, 29, 37, 45, 47, 'MAERZ_AND_PAUL'), |
| 463 | ('Field drab', '#6C541E', 42, 33, 12, 42, 57, 27, 72, 42, 'ISCC_NBS'), |
| 464 | ('Fiery rose', '#FF5470', 100, 33, 44, 350, 100, 67, 67, 100, 'CRAYOLA'), |
| 465 | ('Finn', '#683068', 41, 19, 41, 300, 37, 30, 54, 41, 'HEXCOLOR_CO'), |
| 466 | ('Firebrick', '#B22222', 70, 13, 13, 0, 68, 42, 81, 70, 'X11_WEB'), |
| 467 | ('Fire engine red', '#CE2029', 81, 13, 16, 357, 73, 47, 84, 81, 'FINDTHEDATA_COM'), |
| 468 | ('Flame', '#E25822', 89, 35, 13, 17, 77, 51, 85, 89, 'ISCC_NBS'), |
| 469 | ('Flax', '#EEDC82', 93, 86, 51, 50, 76, 72, 45, 93, 'MAERZ_AND_PAUL'), |
| 470 | ('Flirt', '#A2006D', 64, 0, 43, 320, 100, 32, 100, 64, 'XONA_COM'), |
| 471 | ('Floral white', '#FFFAF0', 100, 98, 94, 40, 100, 97, 6, 100, 'X11_WEB'), |
| 472 | ('Forest green (web)', '#228B22', 13, 55, 13, 120, 61, 34, 76, 55, 'X11_WEB'), |
| 473 | ('French beige', '#A67B5B', 65, 48, 36, 26, 30, 50, 45, 65, 'ISCC_NBS'), |
| 474 | ('French bistre', '#856D4D', 52, 43, 30, 34, 27, 41, 42, 52, 'POURPRE_COM'), |
| 475 | ('French blue', '#0072BB', 0, 45, 73, 203, 100, 37, 100, 73, 'MAERZ_AND_PAUL'), |
| 476 | ('French fuchsia', '#FD3F92', 99, 25, 57, 334, 98, 62, 75, 99, 'POURPRE_COM'), |
| 477 | ('French lilac', '#86608E', 53, 38, 56, 290, 19, 47, 32, 56, 'ISCC_NBS'), |
| 478 | ('French lime', '#9EFD38', 62, 99, 22, 89, 98, 61, 78, 99, 'POURPRE_COM'), |
| 479 | ('French mauve', '#D473D4', 83, 45, 83, 300, 53, 64, 46, 83, 'POURPRE_COM'), |
| 480 | ('French pink', '#FD6C9E', 99, 42, 62, 339, 97, 71, 57, 99, 'POURPRE_COM'), |
| 481 | ('French raspberry', '#C72C48', 78, 17, 28, 349, 64, 48, 78, 78, 'POURPRE_COM'), |
| 482 | ('French sky blue', '#77B5FE', 47, 71, 100, 212, 99, 73, 53, 100, 'POURPRE_COM'), |
| 483 | ('French violet', '#8806CE', 53, 2, 81, 279, 94, 42, 97, 81, 'POURPRE_COM'), |
| 484 | ('Frostbite', '#E936A7', 91, 21, 65, 322, 80, 56, 77, 91, 'CRAYOLA'), |
| 485 | ('Fuchsia', '#FF00FF', 100, 0, 100, 300, 100, 50, 100, 100, 'X11_WEB'), |
| 486 | ('Fuchsia (Crayola)', '#C154C1', 76, 33, 76, 300, 47, 54, 56, 76, 'CRAYOLA'), |
| 487 | ('Fulvous', '#E48400', 89, 52, 0, 35, 100, 45, 100, 89, '99COLORS_NET'), |
| 488 | ('Fuzzy Wuzzy', '#87421F', 53, 26, 12, 20, 63, 33, 77, 53, 'CRAYOLA'); |
| 489 | `.trim(), |
| 490 | }, |
| 491 | { |
| 492 | id: 12, |
| 493 | type: 'quickstart', |
| 494 | title: 'Slack Clone', |
| 495 | description: 'Build a basic slack clone with Row Level Security.', |
| 496 | sql: ` |
| 497 | -- |
| 498 | -- For use with https://github.com/briven/briven/tree/master/examples/slack-clone/nextjs-slack-clone |
| 499 | |
| 500 | -- Custom types |
| 501 | create type public.app_permission as enum ('channels.delete', 'messages.delete'); |
| 502 | create type public.app_role as enum ('admin', 'moderator'); |
| 503 | create type public.user_status as enum ('ONLINE', 'OFFLINE'); |
| 504 | |
| 505 | -- USERS |
| 506 | create table public.users ( |
| 507 | id uuid not null primary key, -- UUID from auth.users |
| 508 | username text, |
| 509 | status user_status default 'OFFLINE'::public.user_status |
| 510 | ); |
| 511 | comment on table public.users is 'Profile data for each user.'; |
| 512 | comment on column public.users.id is 'References the internal Briven Auth user.'; |
| 513 | |
| 514 | -- CHANNELS |
| 515 | create table public.channels ( |
| 516 | id bigint generated by default as identity primary key, |
| 517 | inserted_at timestamp with time zone default timezone('utc'::text, now()) not null, |
| 518 | slug text not null unique, |
| 519 | created_by uuid references public.users not null |
| 520 | ); |
| 521 | comment on table public.channels is 'Topics and groups.'; |
| 522 | |
| 523 | -- MESSAGES |
| 524 | create table public.messages ( |
| 525 | id bigint generated by default as identity primary key, |
| 526 | inserted_at timestamp with time zone default timezone('utc'::text, now()) not null, |
| 527 | message text, |
| 528 | user_id uuid references public.users not null, |
| 529 | channel_id bigint references public.channels on delete cascade not null |
| 530 | ); |
| 531 | comment on table public.messages is 'Individual messages sent by each user.'; |
| 532 | |
| 533 | -- USER ROLES |
| 534 | create table public.user_roles ( |
| 535 | id bigint generated by default as identity primary key, |
| 536 | user_id uuid references public.users on delete cascade not null, |
| 537 | role app_role not null, |
| 538 | unique (user_id, role) |
| 539 | ); |
| 540 | comment on table public.user_roles is 'Application roles for each user.'; |
| 541 | |
| 542 | -- ROLE PERMISSIONS |
| 543 | create table public.role_permissions ( |
| 544 | id bigint generated by default as identity primary key, |
| 545 | role app_role not null, |
| 546 | permission app_permission not null, |
| 547 | unique (role, permission) |
| 548 | ); |
| 549 | comment on table public.role_permissions is 'Application permissions for each role.'; |
| 550 | |
| 551 | -- authorize with role-based access control (RBAC) |
| 552 | create function public.authorize( |
| 553 | requested_permission app_permission, |
| 554 | user_id uuid |
| 555 | ) |
| 556 | returns boolean as |
| 557 | $$ |
| 558 | declare |
| 559 | bind_permissions int; |
| 560 | begin |
| 561 | select |
| 562 | count(*) |
| 563 | from public.role_permissions |
| 564 | inner join public.user_roles on role_permissions.role = user_roles.role |
| 565 | where |
| 566 | role_permissions.permission = authorize.requested_permission and |
| 567 | user_roles.user_id = authorize.user_id |
| 568 | into bind_permissions; |
| 569 | |
| 570 | return bind_permissions > 0; |
| 571 | end; |
| 572 | $$ |
| 573 | language plpgsql security definer; |
| 574 | |
| 575 | -- Secure the tables |
| 576 | alter table public.users |
| 577 | enable row level security; |
| 578 | alter table public.channels |
| 579 | enable row level security; |
| 580 | alter table public.messages |
| 581 | enable row level security; |
| 582 | alter table public.user_roles |
| 583 | enable row level security; |
| 584 | alter table public.role_permissions |
| 585 | enable row level security; |
| 586 | |
| 587 | create policy "Allow logged-in read access" on public.users |
| 588 | for select using (auth.role() = 'authenticated'); |
| 589 | create policy "Allow individual insert access" on public.users |
| 590 | for insert with check ((select auth.uid()) = id); |
| 591 | create policy "Allow individual update access" on public.users |
| 592 | for update using ( (select auth.uid()) = id ); |
| 593 | create policy "Allow logged-in read access" on public.channels |
| 594 | for select using (auth.role() = 'authenticated'); |
| 595 | create policy "Allow individual insert access" on public.channels |
| 596 | for insert with check ((select auth.uid()) = created_by); |
| 597 | create policy "Allow individual delete access" on public.channels |
| 598 | for delete using ((select auth.uid()) = created_by); |
| 599 | create policy "Allow authorized delete access" on public.channels |
| 600 | for delete using (authorize('channels.delete', auth.uid())); |
| 601 | create policy "Allow logged-in read access" on public.messages |
| 602 | for select using (auth.role() = 'authenticated'); |
| 603 | create policy "Allow individual insert access" on public.messages |
| 604 | for insert with check ((select auth.uid()) = user_id); |
| 605 | create policy "Allow individual update access" on public.messages |
| 606 | for update using ((select auth.uid()) = user_id); |
| 607 | create policy "Allow individual delete access" on public.messages |
| 608 | for delete using ((select auth.uid()) = user_id); |
| 609 | create policy "Allow authorized delete access" on public.messages |
| 610 | for delete using (authorize('messages.delete', auth.uid())); |
| 611 | create policy "Allow individual read access" on public.user_roles |
| 612 | for select using ((select auth.uid()) = user_id); |
| 613 | |
| 614 | -- Send "previous data" on change |
| 615 | alter table public.users |
| 616 | replica identity full; |
| 617 | alter table public.channels |
| 618 | replica identity full; |
| 619 | alter table public.messages |
| 620 | replica identity full; |
| 621 | |
| 622 | -- inserts a row into public.users and assigns roles |
| 623 | create function public.handle_new_user() |
| 624 | returns trigger |
| 625 | set search_path = '' |
| 626 | as $$ |
| 627 | declare is_admin boolean; |
| 628 | begin |
| 629 | insert into public.users (id, username) |
| 630 | values (new.id, new.email); |
| 631 | |
| 632 | select count(*) = 1 from auth.users into is_admin; |
| 633 | |
| 634 | if position('+supaadmin@' in new.email) > 0 then |
| 635 | insert into public.user_roles (user_id, role) values (new.id, 'admin'); |
| 636 | elsif position('+supamod@' in new.email) > 0 then |
| 637 | insert into public.user_roles (user_id, role) values (new.id, 'moderator'); |
| 638 | end if; |
| 639 | |
| 640 | return new; |
| 641 | end; |
| 642 | $$ language plpgsql security definer; |
| 643 | |
| 644 | -- trigger the function every time a user is created |
| 645 | create trigger on_auth_user_created |
| 646 | after insert on auth.users |
| 647 | for each row execute procedure public.handle_new_user(); |
| 648 | |
| 649 | /** |
| 650 | * REALTIME SUBSCRIPTIONS |
| 651 | * Only allow realtime listening on public tables. |
| 652 | */ |
| 653 | |
| 654 | begin; |
| 655 | -- remove the realtime publication |
| 656 | drop publication if exists briven_realtime; |
| 657 | |
| 658 | -- re-create the publication but don't enable it for any tables |
| 659 | create publication briven_realtime; |
| 660 | commit; |
| 661 | |
| 662 | -- add tables to the publication |
| 663 | alter publication briven_realtime add table public.channels; |
| 664 | alter publication briven_realtime add table public.messages; |
| 665 | alter publication briven_realtime add table public.users; |
| 666 | |
| 667 | -- DUMMY DATA |
| 668 | insert into public.users (id, username) |
| 669 | values |
| 670 | ('8d0fd2b3-9ca7-4d9e-a95f-9e13dded323e', 'supabot'); |
| 671 | |
| 672 | insert into public.channels (slug, created_by) |
| 673 | values |
| 674 | ('public', '8d0fd2b3-9ca7-4d9e-a95f-9e13dded323e'), |
| 675 | ('random', '8d0fd2b3-9ca7-4d9e-a95f-9e13dded323e'); |
| 676 | |
| 677 | insert into public.messages (message, channel_id, user_id) |
| 678 | values |
| 679 | ('Hello World 👋', 1, '8d0fd2b3-9ca7-4d9e-a95f-9e13dded323e'), |
| 680 | ('Perfection is attained, not when there is nothing more to add, but when there is nothing left to take away.', 2, '8d0fd2b3-9ca7-4d9e-a95f-9e13dded323e'); |
| 681 | |
| 682 | insert into public.role_permissions (role, permission) |
| 683 | values |
| 684 | ('admin', 'channels.delete'), |
| 685 | ('admin', 'messages.delete'), |
| 686 | ('moderator', 'messages.delete'); |
| 687 | `.trim(), |
| 688 | }, |
| 689 | { |
| 690 | id: 13, |
| 691 | type: 'quickstart', |
| 692 | title: 'Todo List', |
| 693 | description: 'Build a basic todo list with Row Level Security.', |
| 694 | sql: ` |
| 695 | -- |
| 696 | -- For use with: |
| 697 | -- https://github.com/briven/briven/tree/master/examples/todo-list/sveltejs-todo-list or |
| 698 | -- https://github.com/briven/examples-archive/tree/main/briven-js-v1/todo-list |
| 699 | -- |
| 700 | |
| 701 | create table todos ( |
| 702 | id bigint generated by default as identity primary key, |
| 703 | user_id uuid references auth.users not null, |
| 704 | task text check (char_length(task) > 3), |
| 705 | is_complete boolean default false, |
| 706 | inserted_at timestamp with time zone default timezone('utc'::text, now()) not null |
| 707 | ); |
| 708 | alter table todos enable row level security; |
| 709 | create policy "Individuals can create todos." on todos for |
| 710 | insert with check (auth.uid() = user_id); |
| 711 | create policy "Individuals can view their own todos. " on todos for |
| 712 | select using ((select auth.uid()) = user_id); |
| 713 | create policy "Individuals can update their own todos." on todos for |
| 714 | update using ((select auth.uid()) = user_id); |
| 715 | create policy "Individuals can delete their own todos." on todos for |
| 716 | delete using ((select auth.uid()) = user_id); |
| 717 | `.trim(), |
| 718 | }, |
| 719 | { |
| 720 | id: 14, |
| 721 | type: 'quickstart', |
| 722 | title: 'Stripe Subscriptions', |
| 723 | description: 'Starter template for the Next.js Stripe Subscriptions Starter.', |
| 724 | sql: ` |
| 725 | /** |
| 726 | * USERS |
| 727 | * Note: This table contains user data. Users should only be able to view and update their own data. |
| 728 | */ |
| 729 | create table users ( |
| 730 | -- UUID from auth.users |
| 731 | id uuid references auth.users not null primary key, |
| 732 | full_name text, |
| 733 | avatar_url text, |
| 734 | -- The customer's billing address, stored in JSON format. |
| 735 | billing_address jsonb, |
| 736 | -- Stores your customer's payment instruments. |
| 737 | payment_method jsonb |
| 738 | ); |
| 739 | alter table users |
| 740 | enable row level security; |
| 741 | create policy "Can view own user data." on users |
| 742 | for select using ((select auth.uid()) = id); |
| 743 | create policy "Can update own user data." on users |
| 744 | for update using ((select auth.uid()) = id); |
| 745 | |
| 746 | /** |
| 747 | * This trigger automatically creates a user entry when a new user signs up via Briven Auth. |
| 748 | */ |
| 749 | create function public.handle_new_user() |
| 750 | returns trigger |
| 751 | set search_path = '' |
| 752 | as $$ |
| 753 | begin |
| 754 | insert into public.users (id, full_name, avatar_url) |
| 755 | values (new.id, new.raw_user_meta_data->>'full_name', new.raw_user_meta_data->>'avatar_url'); |
| 756 | return new; |
| 757 | end; |
| 758 | $$ |
| 759 | language plpgsql security definer; |
| 760 | |
| 761 | create trigger on_auth_user_created |
| 762 | after insert on auth.users |
| 763 | for each row |
| 764 | execute procedure public.handle_new_user(); |
| 765 | |
| 766 | /** |
| 767 | * CUSTOMERS |
| 768 | * Note: this is a private table that contains a mapping of user IDs to Stripe customer IDs. |
| 769 | */ |
| 770 | create table customers ( |
| 771 | -- UUID from auth.users |
| 772 | id uuid references auth.users not null primary key, |
| 773 | -- The user's customer ID in Stripe. User must not be able to update this. |
| 774 | stripe_customer_id text |
| 775 | ); |
| 776 | alter table customers enable row level security; |
| 777 | -- No policies as this is a private table that the user must not have access to. |
| 778 | |
| 779 | /** |
| 780 | * PRODUCTS |
| 781 | * Note: products are created and managed in Stripe and synced to our DB via Stripe webhooks. |
| 782 | */ |
| 783 | create table products ( |
| 784 | -- Product ID from Stripe, e.g. prod_1234. |
| 785 | id text primary key, |
| 786 | -- Whether the product is currently available for purchase. |
| 787 | active boolean, |
| 788 | -- The product's name, meant to be displayable to the customer. Whenever this product is sold via a subscription, name will show up on associated invoice line item descriptions. |
| 789 | name text, |
| 790 | -- The product's description, meant to be displayable to the customer. Use this field to optionally store a long form explanation of the product being sold for your own rendering purposes. |
| 791 | description text, |
| 792 | -- A URL of the product image in Stripe, meant to be displayable to the customer. |
| 793 | image text, |
| 794 | -- Set of key-value pairs, used to store additional information about the object in a structured format. |
| 795 | metadata jsonb |
| 796 | ); |
| 797 | alter table products |
| 798 | enable row level security; |
| 799 | create policy "Allow public read-only access." on products |
| 800 | for select using (true); |
| 801 | |
| 802 | /** |
| 803 | * PRICES |
| 804 | * Note: prices are created and managed in Stripe and synced to our DB via Stripe webhooks. |
| 805 | */ |
| 806 | create type pricing_type as enum ('one_time', 'recurring'); |
| 807 | create type pricing_plan_interval as enum ('day', 'week', 'month', 'year'); |
| 808 | create table prices ( |
| 809 | -- Price ID from Stripe, e.g. price_1234. |
| 810 | id text primary key, |
| 811 | -- The ID of the prduct that this price belongs to. |
| 812 | product_id text references products, |
| 813 | -- Whether the price can be used for new purchases. |
| 814 | active boolean, |
| 815 | -- A brief description of the price. |
| 816 | description text, |
| 817 | -- The unit amount as a positive integer in the smallest currency unit (e.g., 100 cents for US$1.00 or 100 for ¥100, a zero-decimal currency). |
| 818 | unit_amount bigint, |
| 819 | -- Three-letter ISO currency code, in lowercase. |
| 820 | currency text check (char_length(currency) = 3), |
| 821 | -- One of \`one_time\` or \`recurring\` depending on whether the price is for a one-time purchase or a recurring (subscription) purchase. |
| 822 | type pricing_type, |
| 823 | -- The frequency at which a subscription is billed. One of \`day\`, \`week\`, \`month\` or \`year\`. |
| 824 | interval pricing_plan_interval, |
| 825 | -- The number of intervals (specified in the \`interval\` attribute) between subscription billings. For example, \`interval=month\` and \`interval_count=3\` bills every 3 months. |
| 826 | interval_count integer, |
| 827 | -- Default number of trial days when subscribing a customer to this price using [\`trial_from_plan=true\`](https://stripe.com/docs/api#create_subscription-trial_from_plan). |
| 828 | trial_period_days integer, |
| 829 | -- Set of key-value pairs, used to store additional information about the object in a structured format. |
| 830 | metadata jsonb |
| 831 | ); |
| 832 | alter table prices |
| 833 | enable row level security; |
| 834 | create policy "Allow public read-only access." on prices |
| 835 | for select using (true); |
| 836 | |
| 837 | /** |
| 838 | * SUBSCRIPTIONS |
| 839 | * Note: subscriptions are created and managed in Stripe and synced to our DB via Stripe webhooks. |
| 840 | */ |
| 841 | create type subscription_status as enum ('trialing', 'active', 'canceled', 'incomplete', 'incomplete_expired', 'past_due', 'unpaid'); |
| 842 | create table subscriptions ( |
| 843 | -- Subscription ID from Stripe, e.g. sub_1234. |
| 844 | id text primary key, |
| 845 | user_id uuid references auth.users not null, |
| 846 | -- The status of the subscription object, one of subscription_status type above. |
| 847 | status subscription_status, |
| 848 | -- Set of key-value pairs, used to store additional information about the object in a structured format. |
| 849 | metadata jsonb, |
| 850 | -- ID of the price that created this subscription. |
| 851 | price_id text references prices, |
| 852 | -- Quantity multiplied by the unit amount of the price creates the amount of the subscription. Can be used to charge multiple seats. |
| 853 | quantity integer, |
| 854 | -- If true the subscription has been canceled by the user and will be deleted at the end of the billing period. |
| 855 | cancel_at_period_end boolean, |
| 856 | -- Time at which the subscription was created. |
| 857 | created timestamp with time zone default timezone('utc'::text, now()) not null, |
| 858 | -- Start of the current period that the subscription has been invoiced for. |
| 859 | current_period_start timestamp with time zone default timezone('utc'::text, now()) not null, |
| 860 | -- End of the current period that the subscription has been invoiced for. At the end of this period, a new invoice will be created. |
| 861 | current_period_end timestamp with time zone default timezone('utc'::text, now()) not null, |
| 862 | -- If the subscription has ended, the timestamp of the date the subscription ended. |
| 863 | ended_at timestamp with time zone default timezone('utc'::text, now()), |
| 864 | -- A date in the future at which the subscription will automatically get canceled. |
| 865 | cancel_at timestamp with time zone default timezone('utc'::text, now()), |
| 866 | -- If the subscription has been canceled, the date of that cancellation. If the subscription was canceled with \`cancel_at_period_end\`, \`canceled_at\` will still reflect the date of the initial cancellation request, not the end of the subscription period when the subscription is automatically moved to a canceled state. |
| 867 | canceled_at timestamp with time zone default timezone('utc'::text, now()), |
| 868 | -- If the subscription has a trial, the beginning of that trial. |
| 869 | trial_start timestamp with time zone default timezone('utc'::text, now()), |
| 870 | -- If the subscription has a trial, the end of that trial. |
| 871 | trial_end timestamp with time zone default timezone('utc'::text, now()) |
| 872 | ); |
| 873 | alter table subscriptions |
| 874 | enable row level security; |
| 875 | create policy "Can only view own subs data." on subscriptions |
| 876 | for select using ((select auth.uid()) = user_id); |
| 877 | |
| 878 | /** |
| 879 | * REALTIME SUBSCRIPTIONS |
| 880 | * Only allow realtime listening on public tables. |
| 881 | */ |
| 882 | drop publication if exists briven_realtime; |
| 883 | create publication briven_realtime |
| 884 | for table products, prices; |
| 885 | `.trim(), |
| 886 | }, |
| 887 | { |
| 888 | id: 15, |
| 889 | type: 'quickstart', |
| 890 | title: 'User Management Starter', |
| 891 | description: 'Sets up a public Profiles table which you can access with your API.', |
| 892 | sql: ` |
| 893 | -- Create a table for public profiles |
| 894 | create table profiles ( |
| 895 | id uuid references auth.users on delete cascade not null primary key, |
| 896 | updated_at timestamp with time zone, |
| 897 | username text unique, |
| 898 | full_name text, |
| 899 | avatar_url text, |
| 900 | website text, |
| 901 | |
| 902 | constraint username_length check (char_length(username) >= 3) |
| 903 | ); |
| 904 | -- Set up Row Level Security (RLS) |
| 905 | -- See ${DOCS_URL}/guides/auth/row-level-security for more details. |
| 906 | alter table profiles |
| 907 | enable row level security; |
| 908 | |
| 909 | create policy "Public profiles are viewable by everyone." on profiles |
| 910 | for select using (true); |
| 911 | |
| 912 | create policy "Users can insert their own profile." on profiles |
| 913 | for insert with check ((select auth.uid()) = id); |
| 914 | |
| 915 | create policy "Users can update own profile." on profiles |
| 916 | for update using ((select auth.uid()) = id); |
| 917 | |
| 918 | -- This trigger automatically creates a profile entry when a new user signs up via Briven Auth. |
| 919 | -- See ${DOCS_URL}/guides/auth/managing-user-data#using-triggers for more details. |
| 920 | create function public.handle_new_user() |
| 921 | returns trigger |
| 922 | set search_path = '' |
| 923 | as $$ |
| 924 | begin |
| 925 | insert into public.profiles (id, full_name, avatar_url) |
| 926 | values (new.id, new.raw_user_meta_data->>'full_name', new.raw_user_meta_data->>'avatar_url'); |
| 927 | return new; |
| 928 | end; |
| 929 | $$ language plpgsql security definer; |
| 930 | create trigger on_auth_user_created |
| 931 | after insert on auth.users |
| 932 | for each row execute procedure public.handle_new_user(); |
| 933 | |
| 934 | -- Set up Storage! |
| 935 | insert into storage.buckets (id, name) |
| 936 | values ('avatars', 'avatars'); |
| 937 | |
| 938 | -- Set up access controls for storage. |
| 939 | -- See ${DOCS_URL}/guides/storage#policy-examples for more details. |
| 940 | create policy "Avatar images are publicly accessible." on storage.objects |
| 941 | for select using (bucket_id = 'avatars'); |
| 942 | |
| 943 | create policy "Anyone can upload an avatar." on storage.objects |
| 944 | for insert with check (bucket_id = 'avatars'); |
| 945 | `.trim(), |
| 946 | }, |
| 947 | { |
| 948 | id: 16, |
| 949 | type: 'quickstart', |
| 950 | title: 'NextAuth Schema Setup', |
| 951 | description: 'Sets up a the Schema and Tables for the NextAuth Briven Adapter.', |
| 952 | sql: ` |
| 953 | -- |
| 954 | -- Name: next_auth; Type: SCHEMA; |
| 955 | -- |
| 956 | CREATE SCHEMA next_auth; |
| 957 | |
| 958 | GRANT USAGE ON SCHEMA next_auth TO service_role; |
| 959 | GRANT ALL ON SCHEMA next_auth TO postgres; |
| 960 | |
| 961 | -- |
| 962 | -- Create users table |
| 963 | -- |
| 964 | CREATE TABLE IF NOT EXISTS next_auth.users |
| 965 | ( |
| 966 | id uuid NOT NULL DEFAULT gen_random_uuid(), |
| 967 | name text, |
| 968 | email text, |
| 969 | "emailVerified" timestamp with time zone, |
| 970 | image text, |
| 971 | CONSTRAINT users_pkey PRIMARY KEY (id), |
| 972 | CONSTRAINT email_unique UNIQUE (email) |
| 973 | ); |
| 974 | |
| 975 | GRANT ALL ON TABLE next_auth.users TO postgres; |
| 976 | GRANT ALL ON TABLE next_auth.users TO service_role; |
| 977 | |
| 978 | --- uid() function to be used in RLS policies |
| 979 | CREATE FUNCTION next_auth.uid() RETURNS uuid |
| 980 | LANGUAGE sql STABLE |
| 981 | AS $$ |
| 982 | select |
| 983 | coalesce( |
| 984 | nullif(current_setting('request.jwt.claim.sub', true), ''), |
| 985 | (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'sub') |
| 986 | )::uuid |
| 987 | $$; |
| 988 | |
| 989 | -- |
| 990 | -- Create sessions table |
| 991 | -- |
| 992 | CREATE TABLE IF NOT EXISTS next_auth.sessions |
| 993 | ( |
| 994 | id uuid NOT NULL DEFAULT gen_random_uuid(), |
| 995 | expires timestamp with time zone NOT NULL, |
| 996 | "sessionToken" text NOT NULL, |
| 997 | "userId" uuid, |
| 998 | CONSTRAINT sessions_pkey PRIMARY KEY (id), |
| 999 | CONSTRAINT sessionToken_unique UNIQUE ("sessionToken"), |
| 1000 | CONSTRAINT "sessions_userId_fkey" FOREIGN KEY ("userId") |
| 1001 | REFERENCES next_auth.users (id) MATCH SIMPLE |
| 1002 | ON UPDATE NO ACTION |
| 1003 | ON DELETE CASCADE |
| 1004 | ); |
| 1005 | |
| 1006 | GRANT ALL ON TABLE next_auth.sessions TO postgres; |
| 1007 | GRANT ALL ON TABLE next_auth.sessions TO service_role; |
| 1008 | |
| 1009 | -- |
| 1010 | -- Create accounts table |
| 1011 | -- |
| 1012 | CREATE TABLE IF NOT EXISTS next_auth.accounts |
| 1013 | ( |
| 1014 | id uuid NOT NULL DEFAULT gen_random_uuid(), |
| 1015 | type text NOT NULL, |
| 1016 | provider text NOT NULL, |
| 1017 | "providerAccountId" text NOT NULL, |
| 1018 | refresh_token text, |
| 1019 | access_token text, |
| 1020 | expires_at bigint, |
| 1021 | token_type text, |
| 1022 | scope text, |
| 1023 | id_token text, |
| 1024 | session_state text, |
| 1025 | oauth_token_secret text, |
| 1026 | oauth_token text, |
| 1027 | "userId" uuid, |
| 1028 | CONSTRAINT accounts_pkey PRIMARY KEY (id), |
| 1029 | CONSTRAINT provider_unique UNIQUE (provider, "providerAccountId"), |
| 1030 | CONSTRAINT "accounts_userId_fkey" FOREIGN KEY ("userId") |
| 1031 | REFERENCES next_auth.users (id) MATCH SIMPLE |
| 1032 | ON UPDATE NO ACTION |
| 1033 | ON DELETE CASCADE |
| 1034 | ); |
| 1035 | |
| 1036 | GRANT ALL ON TABLE next_auth.accounts TO postgres; |
| 1037 | GRANT ALL ON TABLE next_auth.accounts TO service_role; |
| 1038 | |
| 1039 | -- |
| 1040 | -- Create verification_tokens table |
| 1041 | -- |
| 1042 | CREATE TABLE IF NOT EXISTS next_auth.verification_tokens |
| 1043 | ( |
| 1044 | identifier text, |
| 1045 | token text, |
| 1046 | expires timestamp with time zone NOT NULL, |
| 1047 | CONSTRAINT verification_tokens_pkey PRIMARY KEY (token), |
| 1048 | CONSTRAINT token_unique UNIQUE (token), |
| 1049 | CONSTRAINT token_identifier_unique UNIQUE (token, identifier) |
| 1050 | ); |
| 1051 | |
| 1052 | GRANT ALL ON TABLE next_auth.verification_tokens TO postgres; |
| 1053 | GRANT ALL ON TABLE next_auth.verification_tokens TO service_role; |
| 1054 | `.trim(), |
| 1055 | }, |
| 1056 | { |
| 1057 | id: 17, |
| 1058 | type: 'template', |
| 1059 | title: 'Most frequently invoked', |
| 1060 | description: 'Most frequently called queries in your database.', |
| 1061 | sql: `-- Most frequently called queries |
| 1062 | |
| 1063 | -- A limit of 100 has been added below |
| 1064 | |
| 1065 | select |
| 1066 | auth.rolname, |
| 1067 | statements.query, |
| 1068 | statements.calls, |
| 1069 | -- -- Postgres 13, 14, 15 |
| 1070 | statements.total_exec_time + statements.total_plan_time as total_time, |
| 1071 | statements.min_exec_time + statements.min_plan_time as min_time, |
| 1072 | statements.max_exec_time + statements.max_plan_time as max_time, |
| 1073 | statements.mean_exec_time + statements.mean_plan_time as mean_time, |
| 1074 | -- -- Postgres <= 12 |
| 1075 | -- total_time, |
| 1076 | -- min_time, |
| 1077 | -- max_time, |
| 1078 | -- mean_time, |
| 1079 | statements.rows / statements.calls as avg_rows |
| 1080 | |
| 1081 | from pg_stat_statements as statements |
| 1082 | inner join pg_authid as auth on statements.userid = auth.oid |
| 1083 | order by |
| 1084 | statements.calls desc |
| 1085 | limit |
| 1086 | 100;`, |
| 1087 | }, |
| 1088 | { |
| 1089 | id: 18, |
| 1090 | type: 'template', |
| 1091 | title: 'Most time consuming', |
| 1092 | description: 'Aggregate time spent on a query type.', |
| 1093 | sql: `-- Most time consuming queries |
| 1094 | |
| 1095 | -- A limit of 100 has been added below |
| 1096 | |
| 1097 | select |
| 1098 | auth.rolname, |
| 1099 | statements.query, |
| 1100 | statements.calls, |
| 1101 | statements.total_exec_time + statements.total_plan_time as total_time, |
| 1102 | to_char(((statements.total_exec_time + statements.total_plan_time)/sum(statements.total_exec_time + statements.total_plan_time) over()) * 100, 'FM90D0') || '%' as prop_total_time |
| 1103 | from pg_stat_statements as statements |
| 1104 | inner join pg_authid as auth on statements.userid = auth.oid |
| 1105 | order by |
| 1106 | total_time desc |
| 1107 | limit |
| 1108 | 100;`, |
| 1109 | }, |
| 1110 | { |
| 1111 | id: 19, |
| 1112 | type: 'template', |
| 1113 | title: 'Slowest execution time', |
| 1114 | description: 'Slowest queries based on max execution time.', |
| 1115 | sql: `-- Slowest queries by max execution time |
| 1116 | |
| 1117 | -- A limit of 100 has been added below |
| 1118 | |
| 1119 | select |
| 1120 | auth.rolname, |
| 1121 | statements.query, |
| 1122 | statements.calls, |
| 1123 | -- -- Postgres 13, 14, 15 |
| 1124 | statements.total_exec_time + statements.total_plan_time as total_time, |
| 1125 | statements.min_exec_time + statements.min_plan_time as min_time, |
| 1126 | statements.max_exec_time + statements.max_plan_time as max_time, |
| 1127 | statements.mean_exec_time + statements.mean_plan_time as mean_time, |
| 1128 | -- -- Postgres <= 12 |
| 1129 | -- total_time, |
| 1130 | -- min_time, |
| 1131 | -- max_time, |
| 1132 | -- mean_time, |
| 1133 | statements.rows / statements.calls as avg_rows |
| 1134 | from pg_stat_statements as statements |
| 1135 | inner join pg_authid as auth on statements.userid = auth.oid |
| 1136 | order by |
| 1137 | max_time desc |
| 1138 | limit |
| 1139 | 100;`, |
| 1140 | }, |
| 1141 | { |
| 1142 | id: 20, |
| 1143 | type: 'template', |
| 1144 | title: 'Hit rate', |
| 1145 | description: 'See your cache and index hit rate.', |
| 1146 | sql: `-- Cache and index hit rate |
| 1147 | |
| 1148 | select |
| 1149 | 'index hit rate' as name, |
| 1150 | (sum(idx_blks_hit)) / nullif(sum(idx_blks_hit + idx_blks_read),0) as ratio |
| 1151 | from pg_statio_user_indexes |
| 1152 | union all |
| 1153 | select |
| 1154 | 'table hit rate' as name, |
| 1155 | sum(heap_blks_hit) / nullif(sum(heap_blks_hit) + sum(heap_blks_read),0) as ratio |
| 1156 | from pg_statio_user_tables;`, |
| 1157 | }, |
| 1158 | { |
| 1159 | id: 21, |
| 1160 | type: 'quickstart', |
| 1161 | title: 'OpenAI Vector Search', |
| 1162 | description: 'Template for the Next.js OpenAI Doc Search Starter.', |
| 1163 | sql: ` |
| 1164 | -- Enable pg_vector extension |
| 1165 | create extension if not exists vector with schema public; |
| 1166 | |
| 1167 | -- Create tables |
| 1168 | create table "public"."nods_page" ( |
| 1169 | id bigserial primary key, |
| 1170 | parent_page_id bigint references public.nods_page, |
| 1171 | path text not null unique, |
| 1172 | checksum text, |
| 1173 | meta jsonb, |
| 1174 | type text, |
| 1175 | source text |
| 1176 | ); |
| 1177 | alter table "public"."nods_page" enable row level security; |
| 1178 | |
| 1179 | create table "public"."nods_page_section" ( |
| 1180 | id bigserial primary key, |
| 1181 | page_id bigint not null references public.nods_page on delete cascade, |
| 1182 | content text, |
| 1183 | token_count int, |
| 1184 | embedding vector(1536), |
| 1185 | slug text, |
| 1186 | heading text |
| 1187 | ); |
| 1188 | alter table "public"."nods_page_section" enable row level security; |
| 1189 | |
| 1190 | -- Create embedding similarity search functions |
| 1191 | create or replace function match_page_sections(embedding vector(1536), match_threshold float, match_count int, min_content_length int) |
| 1192 | returns table (id bigint, page_id bigint, slug text, heading text, content text, similarity float) |
| 1193 | language plpgsql |
| 1194 | as $$ |
| 1195 | #variable_conflict use_variable |
| 1196 | begin |
| 1197 | return query |
| 1198 | select |
| 1199 | nods_page_section.id, |
| 1200 | nods_page_section.page_id, |
| 1201 | nods_page_section.slug, |
| 1202 | nods_page_section.heading, |
| 1203 | nods_page_section.content, |
| 1204 | (nods_page_section.embedding <#> embedding) * -1 as similarity |
| 1205 | from nods_page_section |
| 1206 | |
| 1207 | -- We only care about sections that have a useful amount of content |
| 1208 | where length(nods_page_section.content) >= min_content_length |
| 1209 | |
| 1210 | -- The dot product is negative because of a Postgres limitation, so we negate it |
| 1211 | and (nods_page_section.embedding <#> embedding) * -1 > match_threshold |
| 1212 | |
| 1213 | -- OpenAI embeddings are normalized to length 1, so |
| 1214 | -- cosine similarity and dot product will produce the same results. |
| 1215 | -- Using dot product which can be computed slightly faster. |
| 1216 | -- |
| 1217 | -- For the different syntaxes, see https://github.com/pgvector/pgvector |
| 1218 | order by nods_page_section.embedding <#> embedding |
| 1219 | |
| 1220 | limit match_count; |
| 1221 | end; |
| 1222 | $$; |
| 1223 | |
| 1224 | create or replace function get_page_parents(page_id bigint) |
| 1225 | returns table (id bigint, parent_page_id bigint, path text, meta jsonb) |
| 1226 | language sql |
| 1227 | as $$ |
| 1228 | with recursive chain as ( |
| 1229 | select * |
| 1230 | from nods_page |
| 1231 | where id = page_id |
| 1232 | |
| 1233 | union all |
| 1234 | |
| 1235 | select child.* |
| 1236 | from nods_page as child |
| 1237 | join chain on chain.parent_page_id = child.id |
| 1238 | ) |
| 1239 | select id, parent_page_id, path, meta |
| 1240 | from chain; |
| 1241 | $$; |
| 1242 | `.trim(), |
| 1243 | }, |
| 1244 | { |
| 1245 | id: 22, |
| 1246 | type: 'template', |
| 1247 | title: 'Replication status report', |
| 1248 | description: 'See the status of your replication slots and replication lag.', |
| 1249 | sql: `-- Replication status report |
| 1250 | |
| 1251 | SELECT |
| 1252 | s.slot_name, |
| 1253 | s.active, |
| 1254 | COALESCE(r.state, 'N/A') as state, |
| 1255 | COALESCE(r.client_addr, null) as replication_client_address, |
| 1256 | GREATEST(0, ROUND((redo_lsn-restart_lsn)/1024/1024/1024, 2)) as replication_lag_gb |
| 1257 | FROM pg_control_checkpoint(), pg_replication_slots s |
| 1258 | LEFT JOIN pg_stat_replication r ON (r.pid = s.active_pid); |
| 1259 | `, |
| 1260 | }, |
| 1261 | { |
| 1262 | id: 23, |
| 1263 | type: 'quickstart', |
| 1264 | title: 'LangChain', |
| 1265 | description: 'LangChain is a popular framework for working with AI, Vectors, and embeddings.', |
| 1266 | sql: ` |
| 1267 | -- Enable the pgvector extension to work with embedding vectors |
| 1268 | create extension vector; |
| 1269 | |
| 1270 | -- Create a table to store your documents |
| 1271 | create table documents ( |
| 1272 | id bigserial primary key, |
| 1273 | content text, -- corresponds to Document.pageContent |
| 1274 | metadata jsonb, -- corresponds to Document.metadata |
| 1275 | embedding vector(1536) -- 1536 works for OpenAI embeddings, change if needed |
| 1276 | ); |
| 1277 | |
| 1278 | -- Create a function to search for documents |
| 1279 | create function match_documents ( |
| 1280 | query_embedding vector(1536), |
| 1281 | match_count int default null, |
| 1282 | filter jsonb DEFAULT '{}' |
| 1283 | ) returns table ( |
| 1284 | id bigint, |
| 1285 | content text, |
| 1286 | metadata jsonb, |
| 1287 | similarity float |
| 1288 | ) |
| 1289 | language plpgsql |
| 1290 | as $$ |
| 1291 | #variable_conflict use_column |
| 1292 | begin |
| 1293 | return query |
| 1294 | select |
| 1295 | id, |
| 1296 | content, |
| 1297 | metadata, |
| 1298 | 1 - (documents.embedding <=> query_embedding) as similarity |
| 1299 | from documents |
| 1300 | where metadata @> filter |
| 1301 | order by documents.embedding <=> query_embedding |
| 1302 | limit match_count; |
| 1303 | end; |
| 1304 | $$; |
| 1305 | `.trim(), |
| 1306 | }, |
| 1307 | { |
| 1308 | id: 24, |
| 1309 | type: 'template', |
| 1310 | title: 'Install dbdev', |
| 1311 | description: |
| 1312 | 'dbdev is a client for installing Trusted Language Extensions (TLE) into your database.', |
| 1313 | sql: ` |
| 1314 | /*--------------------- |
| 1315 | ---- install dbdev ---- |
| 1316 | ----------------------- |
| 1317 | Requires: |
| 1318 | - pg_tle: https://github.com/aws/pg_tle |
| 1319 | - pgsql-http: https://github.com/pramsey/pgsql-http |
| 1320 | |
| 1321 | Warning: |
| 1322 | Restoring a logical backup of a database with a TLE installed can fail. |
| 1323 | For this reason, dbdev should only be used with databases with physical backups enabled. |
| 1324 | */ |
| 1325 | create extension if not exists http with schema extensions; |
| 1326 | create extension if not exists pg_tle; |
| 1327 | select pgtle.uninstall_extension_if_exists('briven-dbdev'); |
| 1328 | drop extension if exists "briven-dbdev"; |
| 1329 | select |
| 1330 | pgtle.install_extension( |
| 1331 | 'briven-dbdev', |
| 1332 | resp.contents ->> 'version', |
| 1333 | 'PostgreSQL package manager', |
| 1334 | resp.contents ->> 'sql' |
| 1335 | ) |
| 1336 | from http( |
| 1337 | ( |
| 1338 | 'GET', |
| 1339 | 'https://api.database.dev/rest/v1/' |
| 1340 | || 'package_versions?select=sql,version' |
| 1341 | || '&package_name=eq.briven-dbdev' |
| 1342 | || '&order=version.desc' |
| 1343 | || '&limit=1', |
| 1344 | array[ |
| 1345 | ('apiKey', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InhtdXB0cHBsZnZpaWZyYndtbXR2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2ODAxMDczNzIsImV4cCI6MTk5NTY4MzM3Mn0.z2CN0mvO2No8wSi46Gw59DFGCTJrzM0AQKsu_5k134s')::http_header |
| 1346 | ], |
| 1347 | null, |
| 1348 | null |
| 1349 | ) |
| 1350 | ) x, |
| 1351 | lateral ( |
| 1352 | select |
| 1353 | ((row_to_json(x) -> 'content') #>> '{}')::json -> 0 |
| 1354 | ) resp(contents); |
| 1355 | create extension "briven-dbdev"; |
| 1356 | select dbdev.install('briven-dbdev'); |
| 1357 | drop extension if exists "briven-dbdev"; |
| 1358 | create extension "briven-dbdev"; |
| 1359 | `.trim(), |
| 1360 | }, |
| 1361 | { |
| 1362 | id: 25, |
| 1363 | type: 'template', |
| 1364 | title: 'Large objects', |
| 1365 | description: 'List large objects (tables/indexes) in your database.', |
| 1366 | sql: `SELECT |
| 1367 | SCHEMA_NAME, |
| 1368 | relname, |
| 1369 | table_size |
| 1370 | FROM |
| 1371 | (SELECT |
| 1372 | pg_catalog.pg_namespace.nspname AS SCHEMA_NAME, |
| 1373 | relname, |
| 1374 | pg_relation_size(pg_catalog.pg_class.oid) AS table_size |
| 1375 | FROM pg_catalog.pg_class |
| 1376 | JOIN pg_catalog.pg_namespace ON relnamespace = pg_catalog.pg_namespace.oid |
| 1377 | ) t |
| 1378 | WHERE SCHEMA_NAME NOT LIKE 'pg_%' |
| 1379 | ORDER BY table_size DESC |
| 1380 | LIMIT 25`.trim(), |
| 1381 | }, |
| 1382 | { |
| 1383 | id: 26, |
| 1384 | type: 'template', |
| 1385 | title: 'Limit MFA verification attempts to one in 2 seconds', |
| 1386 | description: |
| 1387 | 'Create an Auth hook that limits the number of failed MFA verification attempts to one in 2 seconds.', |
| 1388 | sql: ` |
| 1389 | create function public.hook_mfa_verification_attempt(event jsonb) |
| 1390 | returns jsonb |
| 1391 | language plpgsql |
| 1392 | as $$ |
| 1393 | declare |
| 1394 | last_failed_at timestamp; |
| 1395 | begin |
| 1396 | if event->'valid' is true then |
| 1397 | -- code is valid, accept it |
| 1398 | return jsonb_build_object('decision', 'continue'); |
| 1399 | end if; |
| 1400 | |
| 1401 | select last_failed_at into last_failed_at |
| 1402 | from public.mfa_failed_verification_attempts |
| 1403 | where |
| 1404 | user_id = (event->'user_id')::uuid |
| 1405 | and |
| 1406 | factor_id = event->'factor_id'; |
| 1407 | |
| 1408 | if last_failed_at is not null and now() - last_failed_at < interval '2 seconds' then |
| 1409 | -- last attempt was done too quickly |
| 1410 | return jsonb_build_object( |
| 1411 | 'error', jsonb_build_object( |
| 1412 | 'http_code', 429, |
| 1413 | 'message', 'Please wait a moment before trying again.' |
| 1414 | ) |
| 1415 | ); |
| 1416 | end if; |
| 1417 | |
| 1418 | -- record this failed attempt |
| 1419 | insert into public.mfa_failed_verification_attempts |
| 1420 | ( |
| 1421 | user_id, |
| 1422 | factor_id, |
| 1423 | last_refreshed_at |
| 1424 | ) |
| 1425 | values |
| 1426 | ( |
| 1427 | event->'user_id', |
| 1428 | event->'factor_id', |
| 1429 | now() |
| 1430 | ) |
| 1431 | on conflict do update |
| 1432 | set last_refreshed_at = now(); |
| 1433 | |
| 1434 | -- finally let Briven Auth do the default behavior for a failed attempt |
| 1435 | return jsonb_build_object('decision', 'continue'); |
| 1436 | end; |
| 1437 | $$; |
| 1438 | |
| 1439 | -- Assign appropriate permissions and revoke access |
| 1440 | grant execute |
| 1441 | on function public.hook_mfa_verification_attempt |
| 1442 | to briven_auth_admin; |
| 1443 | |
| 1444 | grant all |
| 1445 | on table public.mfa_failed_verification_attempts |
| 1446 | to briven_auth_admin; |
| 1447 | |
| 1448 | revoke execute |
| 1449 | on function public.hook_mfa_verification_attempt |
| 1450 | from authenticated, anon, public; |
| 1451 | |
| 1452 | revoke all |
| 1453 | on table public.mfa_failed_verification_attempts |
| 1454 | from authenticated, anon, public; |
| 1455 | |
| 1456 | grant usage on schema public to briven_auth_admin;`.trim(), |
| 1457 | }, |
| 1458 | { |
| 1459 | id: 27, |
| 1460 | type: 'template', |
| 1461 | title: 'Add Auth Hook (Password Verification Attempt)', |
| 1462 | description: |
| 1463 | 'Create an Auth Hook that limits number of failed password verification attempts to one in 10 seconds', |
| 1464 | sql: ` |
| 1465 | create function public.hook_password_verification_attempt(event jsonb) |
| 1466 | returns jsonb |
| 1467 | language plpgsql |
| 1468 | as $$ |
| 1469 | declare |
| 1470 | last_failed_at timestamp; |
| 1471 | begin |
| 1472 | if event->'valid' is true then |
| 1473 | -- password is valid, accept it |
| 1474 | return jsonb_build_object('decision', 'continue'); |
| 1475 | end if; |
| 1476 | |
| 1477 | select last_failed_at into last_failed_at |
| 1478 | from public.password_failed_verification_attempts |
| 1479 | where |
| 1480 | user_id = (event->'user_id')::uuid; |
| 1481 | |
| 1482 | if last_failed_at is not null and now() - last_failed_at < interval '10 seconds' then |
| 1483 | -- last attempt was done too quickly |
| 1484 | return jsonb_build_object( |
| 1485 | 'error', jsonb_build_object( |
| 1486 | 'http_code', 429, |
| 1487 | 'message', 'Please wait a moment before trying again.' |
| 1488 | ) |
| 1489 | ); |
| 1490 | end if; |
| 1491 | |
| 1492 | -- record this failed attempt |
| 1493 | insert into public.password_failed_verification_attempts |
| 1494 | ( |
| 1495 | user_id, |
| 1496 | last_failed_at |
| 1497 | ) |
| 1498 | values |
| 1499 | ( |
| 1500 | event->'user_id', |
| 1501 | now() |
| 1502 | ) |
| 1503 | on conflict do update |
| 1504 | set last_failed_at = now(); |
| 1505 | |
| 1506 | -- finally let Briven Auth do the default behavior for a failed attempt |
| 1507 | return jsonb_build_object('decision', 'continue'); |
| 1508 | end; |
| 1509 | $$; |
| 1510 | |
| 1511 | -- Assign appropriate permissions |
| 1512 | grant execute |
| 1513 | on function public.hook_password_verification_attempt |
| 1514 | to briven_auth_admin; |
| 1515 | |
| 1516 | grant all |
| 1517 | on table public.password_failed_verification_attempts |
| 1518 | to briven_auth_admin; |
| 1519 | |
| 1520 | revoke execute |
| 1521 | on function public.hook_password_verification_attempt |
| 1522 | from authenticated, anon, public; |
| 1523 | |
| 1524 | revoke all |
| 1525 | on table public.password_failed_verification_attempts |
| 1526 | from authenticated, anon, public; |
| 1527 | |
| 1528 | grant usage on schema public to briven_auth_admin;`.trim(), |
| 1529 | }, |
| 1530 | { |
| 1531 | id: 28, |
| 1532 | type: 'template', |
| 1533 | title: 'Add Auth Hook (Custom Access Token)', |
| 1534 | description: 'Create an Auth Hook to add custom claims to your Auth Token', |
| 1535 | sql: ` |
| 1536 | -- Assumes that there is an is_admin flag on the profiles table. |
| 1537 | create or replace function public.custom_access_token_hook(event jsonb) |
| 1538 | returns jsonb |
| 1539 | language plpgsql |
| 1540 | as $$ |
| 1541 | declare |
| 1542 | claims jsonb; |
| 1543 | is_admin boolean; |
| 1544 | begin |
| 1545 | -- Check if the user is marked as admin in the profiles table |
| 1546 | select is_admin into is_admin from profiles where user_id = (event->>'user_id')::uuid; |
| 1547 | |
| 1548 | -- Proceed only if the user is an admin |
| 1549 | if is_admin then |
| 1550 | claims := event->'claims'; |
| 1551 | |
| 1552 | -- Check if 'user_metadata' exists in claims |
| 1553 | if jsonb_typeof(claims->'user_metadata') is null then |
| 1554 | -- If 'user_metadata' does not exist, create an empty object |
| 1555 | claims := jsonb_set(claims, '{user_metadata}', '{}'); |
| 1556 | end if; |
| 1557 | |
| 1558 | -- Set a claim of 'admin' |
| 1559 | claims := jsonb_set(claims, '{user_metadata, admin}', 'true'); |
| 1560 | |
| 1561 | -- Update the 'claims' object in the original event |
| 1562 | event := jsonb_set(event, '{claims}', claims); |
| 1563 | end if; |
| 1564 | |
| 1565 | -- Return the modified or original event |
| 1566 | return event; |
| 1567 | end; |
| 1568 | $$; |
| 1569 | |
| 1570 | grant execute |
| 1571 | on function public.custom_access_token_hook |
| 1572 | to briven_auth_admin; |
| 1573 | |
| 1574 | revoke execute |
| 1575 | on function public.custom_access_token_hook |
| 1576 | from authenticated, anon, public; |
| 1577 | |
| 1578 | grant usage on schema public to briven_auth_admin;`.trim(), |
| 1579 | }, |
| 1580 | |
| 1581 | { |
| 1582 | id: 29, |
| 1583 | type: 'template', |
| 1584 | title: 'Add Auth Hook (General)', |
| 1585 | description: 'Create an Auth Hook', |
| 1586 | sql: ` |
| 1587 | create or replace function public.custom_access_token_hook(event jsonb) |
| 1588 | returns jsonb |
| 1589 | language plpgsql |
| 1590 | as $$ |
| 1591 | declare |
| 1592 | -- Insert variables here |
| 1593 | begin |
| 1594 | -- Insert logic here |
| 1595 | return event; |
| 1596 | end; |
| 1597 | $$; |
| 1598 | -- Permissions for the hook |
| 1599 | grant execute on function public.custom_access_token_hook to briven_auth_admin; |
| 1600 | revoke execute on function public.custom_access_token_hook from authenticated, anon, public; |
| 1601 | `, |
| 1602 | }, |
| 1603 | ] |