diff --git a/assets/icons/blocked.svg b/assets/icons/blocked.svg new file mode 100755 index 0000000..7d6d1e4 --- /dev/null +++ b/assets/icons/blocked.svg @@ -0,0 +1,5 @@ + + +blocked + + diff --git a/assets/js/api.js b/assets/js/api.js index 11edb1d..0145318 100644 --- a/assets/js/api.js +++ b/assets/js/api.js @@ -42,7 +42,7 @@ export async function post(path, data, { query } = {}) { return body; } - throw new Error(body.message); + throw new Error(body.statusMessage); } export async function patch(path, data, { query } = {}) { diff --git a/components/posts/post.vue b/components/posts/post.vue new file mode 100644 index 0000000..ac40b2e --- /dev/null +++ b/components/posts/post.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/knexfile.js b/knexfile.js old mode 100755 new mode 100644 index dcef71c..4d28793 --- a/knexfile.js +++ b/knexfile.js @@ -1,6 +1,6 @@ -const config = require('config'); +import config from 'config'; -module.exports = { +export default { client: 'pg', connection: config.database, }; diff --git a/migrations/20230513004141_init.js b/migrations/20230513004141_init.js index 624d43a..3d3fdc4 100644 --- a/migrations/20230513004141_init.js +++ b/migrations/20230513004141_init.js @@ -1,4 +1,16 @@ -exports.up = async function(knex) { +import fs from 'fs'; + +export async function up(knex) { + const nanoidFn = await fs.promises.readFile('./migrations/nanoid.sql', 'utf8'); // from https://github.com/viascom/nanoid-postgres + + await knex.raw(nanoidFn); + + await knex.raw(` + CREATE FUNCTION shack_id(length smallint DEFAULT 8) RETURNS TEXT AS $$ + SELECT nanoid(length, '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'); + $$ LANGUAGE SQL STABLE; + `); + await knex.schema.createTable('users', (table) => { table.increments('id'); @@ -63,7 +75,9 @@ exports.up = async function(knex) { }); await knex.schema.createTable('posts', (table) => { - table.increments('id'); + table.text('id', 8) + .primary() + .defaultTo(knex.raw('shack_id()')); table.text('title') .notNullable(); @@ -86,12 +100,39 @@ exports.up = async function(knex) { .defaultTo(knex.fn.now()); }); - await knex.raw(`ALTER TABLE posts ADD CONSTRAINT post_content CHECK (body IS NOT NULL OR url IS NOT NULL)`); -}; + await knex.raw('ALTER TABLE posts ADD CONSTRAINT post_content CHECK (body IS NOT NULL OR url IS NOT NULL)'); -exports.down = async function(knex) { + await knex.schema.createTable('comments', (table) => { + table.text('id', 8) + .primary() + .defaultTo(knex.raw('shack_id()')); + + table.text('post_id', 8) + .notNullable() + .references('id') + .inTable('posts'); + + table.integer('user_id') + .notNullable() + .references('id') + .inTable('users'); + + table.text('body'); + + table.datetime('created_at') + .notNullable() + .defaultTo(knex.fn.now()); + }); +} + +export async function down(knex) { + await knex.schema.dropTable('comments'); await knex.schema.dropTable('posts'); await knex.schema.dropTable('shelves_settings'); await knex.schema.dropTable('shelves'); await knex.schema.dropTable('users'); -}; + + await knex.raw(` + DROP FUNCTION IF EXISTS shack_id; + `); +} diff --git a/migrations/nanoid.sql b/migrations/nanoid.sql new file mode 100644 index 0000000..63e6bcc --- /dev/null +++ b/migrations/nanoid.sql @@ -0,0 +1,66 @@ +/* + * Copyright 2023 Viascom Ltd liab. Co + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +CREATE EXTENSION IF NOT EXISTS pgcrypto; + +CREATE OR REPLACE FUNCTION nanoid( + size int DEFAULT 21, + alphabet text DEFAULT '_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' +) + RETURNS text + LANGUAGE plpgsql + volatile +AS +$$ +DECLARE + idBuilder text := ''; + counter int := 0; + bytes bytea; + alphabetIndex int; + alphabetArray text[]; + alphabetLength int; + mask int; + step int; +BEGIN + alphabetArray := regexp_split_to_array(alphabet, ''); + alphabetLength := array_length(alphabetArray, 1); + mask := (2 << cast(floor(log(alphabetLength - 1) / log(2)) as int)) - 1; + step := cast(ceil(1.6 * mask * size / alphabetLength) AS int); + + while true + loop + bytes := gen_random_bytes(step); + while counter < step + loop + alphabetIndex := (get_byte(bytes, counter) & mask) + 1; + if alphabetIndex <= alphabetLength then + idBuilder := idBuilder || alphabetArray[alphabetIndex]; + if length(idBuilder) = size then + return idBuilder; + end if; + end if; + counter := counter + 1; + end loop; + + counter := 0; + end loop; +END +$$; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 747a43b..74cf541 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "config": "^3.3.9", "connect-redis": "^7.1.0", "cross-env": "^7.0.3", + "date-fns": "^2.30.0", "error-stack-parser": "^2.1.4", "eslint": "^8.41.0", "eslint-config-airbnb-base": "^15.0.0", @@ -36,6 +37,7 @@ "pg": "^8.11.0", "pinia": "^2.1.3", "redis": "^4.6.6", + "short-uuid": "^4.2.2", "sirv": "^2.0.2", "vite": "^4.0.3", "vite-plugin-ssr": "^0.4.126", @@ -2623,6 +2625,11 @@ "node": ">=4" } }, + "node_modules/any-base": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", + "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==" + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -3381,6 +3388,21 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -6747,6 +6769,26 @@ "node": ">=8" } }, + "node_modules/short-uuid": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/short-uuid/-/short-uuid-4.2.2.tgz", + "integrity": "sha512-IE7hDSGV2U/VZoCsjctKX6l5t5ak2jE0+aeGJi3KtvjIUNuZVmHVYUjNBhmo369FIWGDtaieRaO8A83Lvwfpqw==", + "dependencies": { + "any-base": "^1.1.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/short-uuid/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -9570,6 +9612,11 @@ "color-convert": "^1.9.0" } }, + "any-base": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", + "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==" + }, "anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -10135,6 +10182,14 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, + "date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "requires": { + "@babel/runtime": "^7.21.0" + } + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -12534,6 +12589,22 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, + "short-uuid": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/short-uuid/-/short-uuid-4.2.2.tgz", + "integrity": "sha512-IE7hDSGV2U/VZoCsjctKX6l5t5ak2jE0+aeGJi3KtvjIUNuZVmHVYUjNBhmo369FIWGDtaieRaO8A83Lvwfpqw==", + "requires": { + "any-base": "^1.1.0", + "uuid": "^8.3.2" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", diff --git a/package.json b/package.json index e1267ad..ecaee00 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,9 @@ "build": "vite build", "server": "node --experimental-specifier-resolution=node ./src/web/server", "server:prod": "cross-env NODE_ENV=production node ./src/web/server", - "migrate-make": "knex-migrate generate", - "migrate": "knex-migrate up", - "rollback": "knex-migrate down" + "migrate-make": "knex migrate:make", + "migrate": "knex migrate:latest", + "rollback": "knex migrate:rollback" }, "dependencies": { "@babel/cli": "^7.21.5", @@ -39,6 +39,7 @@ "config": "^3.3.9", "connect-redis": "^7.1.0", "cross-env": "^7.0.3", + "date-fns": "^2.30.0", "error-stack-parser": "^2.1.4", "eslint": "^8.41.0", "eslint-config-airbnb-base": "^15.0.0", @@ -53,6 +54,7 @@ "pg": "^8.11.0", "pinia": "^2.1.3", "redis": "^4.6.6", + "short-uuid": "^4.2.2", "sirv": "^2.0.2", "vite": "^4.0.3", "vite-plugin-ssr": "^0.4.126", diff --git a/pages/account/login.page.vue b/pages/account/login.page.vue index d547aa7..fd494e2 100644 --- a/pages/account/login.page.vue +++ b/pages/account/login.page.vue @@ -33,21 +33,24 @@ -
+ Sign up +