mirror of https://github.com/sylv/micro.git
chore: update deps, minor bug fixes
This commit is contained in:
parent
5c03b8a2fe
commit
fae9136e23
|
@ -17,6 +17,6 @@
|
|||
"clean": "rm -rf ./packages/*/{tsconfig.tsbuildinfo,lib,dist,yarn-error.log,.next}"
|
||||
},
|
||||
"devDependencies": {
|
||||
"turbo": "1.5.5"
|
||||
"turbo": "1.8.8"
|
||||
}
|
||||
}
|
|
@ -1,93 +1,94 @@
|
|||
{
|
||||
"name": "@ryanke/micro-api",
|
||||
"version": "1.0.0",
|
||||
"repository": "https://github.com/sylv/micro.git",
|
||||
"author": "Ryan <ryan@sylver.me>",
|
||||
"license": "GPL-3.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"engine": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"scripts": {
|
||||
"watch": "tsup src/main.ts src/migrations/* --target node16 --watch --clean --format esm --sourcemap --onSuccess \"node dist/main.js --inspect --inspect-brk\"",
|
||||
"build": "rm -rf ./dist && ncc build src/main.ts -o dist --minify --transpile-only --v8-cache --no-source-map-register",
|
||||
"lint": "eslint src --fix --cache",
|
||||
"test": "vitest run"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/cookie": "^8.3.0",
|
||||
"@fastify/helmet": "^10.0.1",
|
||||
"@fastify/multipart": "^7.2.0",
|
||||
"@jenyus-org/nestjs-graphql-utils": "^1.6.4",
|
||||
"@mikro-orm/core": "^5.4.2",
|
||||
"@mikro-orm/migrations": "^5.4.2",
|
||||
"@mikro-orm/nestjs": "^5.1.2",
|
||||
"@mikro-orm/postgresql": "^5.4.2",
|
||||
"@nestjs/common": "^9.1.4",
|
||||
"@nestjs/core": "^9.1.4",
|
||||
"@nestjs/graphql": "^10.0.16",
|
||||
"@nestjs/jwt": "^9.0.0",
|
||||
"@nestjs/mercurius": "^10.0.16",
|
||||
"@nestjs/passport": "^9.0.0",
|
||||
"@nestjs/platform-fastify": "^9.1.4",
|
||||
"@nestjs/schedule": "^2.1.0",
|
||||
"@ryanke/venera": "^1.0.3",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bytes": "^3.1.2",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.13.2",
|
||||
"content-range": "^2.0.2",
|
||||
"dedent": "^0.7.0",
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
"fastify": "^4.7.0",
|
||||
"file-type": "^18.0.0",
|
||||
"fluent-ffmpeg": "^2.1.2",
|
||||
"graphql": "^16.6.0",
|
||||
"handlebars": "^4.7.7",
|
||||
"istextorbinary": "^6.0.0",
|
||||
"luxon": "^3.0.4",
|
||||
"mercurius": "^11.0.1",
|
||||
"mime-types": "^2.1.35",
|
||||
"ms": "^3.0.0-canary.1",
|
||||
"nanoid": "^4.0.0",
|
||||
"nodemailer": "^6.8.0",
|
||||
"normalize-url": "^7.2.0",
|
||||
"otplib": "^12.0.1",
|
||||
"passport": "^0.6.0",
|
||||
"passport-jwt": "^4.0.0",
|
||||
"pretty-bytes": "^6.0.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rxjs": "^7.5.7",
|
||||
"sharp": "^0.31.1",
|
||||
"stream-size": "^0.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@mikro-orm/cli": "^5.4.2",
|
||||
"@swc/core": "^1.3.5",
|
||||
"@sylo-digital/scripts": "^1.0.12",
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/bytes": "^3.1.1",
|
||||
"@types/dedent": "^0.7.0",
|
||||
"@types/fluent-ffmpeg": "^2.1.20",
|
||||
"@types/luxon": "^3.0.1",
|
||||
"@types/mime-types": "^2.1.1",
|
||||
"@types/ms": "^0.7.31",
|
||||
"@types/node": "16",
|
||||
"@types/nodemailer": "^6.4.6",
|
||||
"@types/passport-jwt": "^3.0.7",
|
||||
"@types/sharp": "^0.31.0",
|
||||
"@vercel/ncc": "^0.34.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsup": "^6.2.3",
|
||||
"typescript": "^4.8.4",
|
||||
"vitest": "^0.24.0"
|
||||
},
|
||||
"mikro-orm": {
|
||||
"useTsNode": true,
|
||||
"configPaths": [
|
||||
"./src/orm.ts",
|
||||
"./dist/orm.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
"name": "@ryanke/micro-api",
|
||||
"version": "1.0.0",
|
||||
"repository": "https://github.com/sylv/micro.git",
|
||||
"author": "Ryan <ryan@sylver.me>",
|
||||
"license": "GPL-3.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"engine": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"scripts": {
|
||||
"watch": "tsup src/main.ts src/migrations/* --target node18 --watch --clean --format esm --sourcemap --onSuccess \"node dist/main.js --inspect --inspect-brk\"",
|
||||
"build": "rm -rf ./dist && ncc build src/main.ts -o dist --minify --transpile-only --v8-cache --no-source-map-register",
|
||||
"lint": "eslint src --fix --cache",
|
||||
"test": "vitest run",
|
||||
"mikro-orm": "NODE_OPTIONS=\"--loader ts-node/esm\" mikro-orm"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/cookie": "^8.3.0",
|
||||
"@fastify/helmet": "^10.1.0",
|
||||
"@fastify/multipart": "^7.5.0",
|
||||
"@jenyus-org/graphql-utils": "^1.5.0",
|
||||
"@mercuriusjs/gateway": "^1.2.0",
|
||||
"@mikro-orm/core": "^5.6.15",
|
||||
"@mikro-orm/migrations": "^5.6.15",
|
||||
"@mikro-orm/nestjs": "^5.1.8",
|
||||
"@mikro-orm/postgresql": "^5.6.15",
|
||||
"@nestjs/common": "^9.3.12",
|
||||
"@nestjs/core": "^9.3.12",
|
||||
"@nestjs/graphql": "^11.0.4",
|
||||
"@nestjs/jwt": "^10.0.3",
|
||||
"@nestjs/mercurius": "^11.0.3",
|
||||
"@nestjs/passport": "^9.0.3",
|
||||
"@nestjs/platform-fastify": "^9.3.12",
|
||||
"@nestjs/schedule": "^2.2.0",
|
||||
"@ryanke/venera": "^1.0.5",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bytes": "^3.1.2",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.0",
|
||||
"content-range": "^2.0.2",
|
||||
"dedent": "^0.7.0",
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
"fastify": "^4.15.0",
|
||||
"file-type": "^18.2.1",
|
||||
"fluent-ffmpeg": "^2.1.2",
|
||||
"graphql": "^16.6.0",
|
||||
"handlebars": "^4.7.7",
|
||||
"istextorbinary": "^6.0.0",
|
||||
"luxon": "^3.3.0",
|
||||
"mercurius": "^12.2.0",
|
||||
"mime-types": "^2.1.35",
|
||||
"ms": "^3.0.0-canary.1",
|
||||
"nanoid": "^4.0.2",
|
||||
"nodemailer": "^6.9.1",
|
||||
"normalize-url": "^8.0.0",
|
||||
"otplib": "^12.0.1",
|
||||
"passport": "^0.6.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"pretty-bytes": "^6.1.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rxjs": "^7.8.0",
|
||||
"sharp": "^0.32.0",
|
||||
"stream-size": "^0.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@mikro-orm/cli": "^5.6.15",
|
||||
"@swc/core": "^1.3.44",
|
||||
"@sylo-digital/scripts": "^1.0.12",
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/bytes": "^3.1.1",
|
||||
"@types/dedent": "^0.7.0",
|
||||
"@types/fluent-ffmpeg": "^2.1.21",
|
||||
"@types/luxon": "^3.2.0",
|
||||
"@types/mime-types": "^2.1.1",
|
||||
"@types/ms": "^0.7.31",
|
||||
"@types/node": "^18.15.11",
|
||||
"@types/nodemailer": "^6.4.7",
|
||||
"@types/passport-jwt": "^3.0.8",
|
||||
"@types/sharp": "^0.31.1",
|
||||
"@vercel/ncc": "^0.36.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsup": "^6.7.0",
|
||||
"typescript": "^5.0.3",
|
||||
"vitest": "^0.29.8"
|
||||
},
|
||||
"mikro-orm": {
|
||||
"useTsNode": true,
|
||||
"configPaths": [
|
||||
"./src/orm.config.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -645,12 +645,7 @@
|
|||
"terms-of-service",
|
||||
"terms_of_service",
|
||||
"termsofservice",
|
||||
"test1",
|
||||
"test2",
|
||||
"test3",
|
||||
"teste",
|
||||
"testing",
|
||||
"tests",
|
||||
"theme",
|
||||
"themes",
|
||||
"thread",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { customAlphabet } from 'nanoid';
|
||||
import blocklist from '../blocklist.json';
|
||||
import blocklist from '../blocklist.json' assert { type: 'json' };
|
||||
|
||||
const contentIdLength = 6;
|
||||
const paranoidIdLength = 12;
|
||||
|
|
|
@ -3,7 +3,7 @@ import { MikroORM } from '@mikro-orm/core';
|
|||
import type { EntityManager } from '@mikro-orm/postgresql';
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { checkForOldDatabase, migrateOldDatabase } from './helpers/migrate-old-database.js';
|
||||
import mikroOrmConfig, { migrationsTableName, ormLogger } from './orm.js';
|
||||
import mikroOrmConfig, { MIGRATIONS_TABLE_NAME, ORM_LOGGER } from './orm.config.js';
|
||||
|
||||
const logger = new Logger('migrate');
|
||||
|
||||
|
@ -25,13 +25,13 @@ export const migrate = async (
|
|||
const executedMigrations = await migrator.getExecutedMigrations();
|
||||
const pendingMigrations = await migrator.getPendingMigrations();
|
||||
if (!pendingMigrations[0]) {
|
||||
ormLogger.debug(`No pending migrations, ${executedMigrations.length} already executed`);
|
||||
ORM_LOGGER.debug(`No pending migrations, ${executedMigrations.length} already executed`);
|
||||
return;
|
||||
}
|
||||
|
||||
ormLogger.log(`Migrating through ${pendingMigrations.length} migrations`);
|
||||
ORM_LOGGER.log(`Migrating through ${pendingMigrations.length} migrations`);
|
||||
await em.transactional(async (em) => {
|
||||
if (!skipLock) await em.execute(`LOCK TABLE ${migrationsTableName} IN EXCLUSIVE MODE`);
|
||||
if (!skipLock) await em.execute(`LOCK TABLE ${MIGRATIONS_TABLE_NAME} IN EXCLUSIVE MODE`);
|
||||
await migrator.up({ transaction: em.getTransactionContext() });
|
||||
});
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"default": "0",
|
||||
"mappedType": "integer"
|
||||
},
|
||||
"password": {
|
||||
|
@ -210,178 +211,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(255)",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "string"
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "varchar(255)",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "string"
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "timestamptz(0)",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"mappedType": "datetime"
|
||||
}
|
||||
},
|
||||
"name": "users_verification",
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"keyName": "users_verification_pkey",
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"composite": false,
|
||||
"primary": true,
|
||||
"unique": true
|
||||
}
|
||||
],
|
||||
"checks": [],
|
||||
"foreignKeys": {
|
||||
"users_verification_user_id_foreign": {
|
||||
"constraintName": "users_verification_user_id_foreign",
|
||||
"columnNames": [
|
||||
"user_id"
|
||||
],
|
||||
"localTableName": "public.users_verification",
|
||||
"referencedColumnNames": [
|
||||
"id"
|
||||
],
|
||||
"referencedTableName": "public.users",
|
||||
"deleteRule": "CASCADE",
|
||||
"updateRule": "cascade"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(255)",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "string"
|
||||
},
|
||||
"permissions": {
|
||||
"name": "permissions",
|
||||
"type": "int",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "integer"
|
||||
},
|
||||
"inviter_id": {
|
||||
"name": "inviter_id",
|
||||
"type": "varchar(255)",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "string"
|
||||
},
|
||||
"invited_id": {
|
||||
"name": "invited_id",
|
||||
"type": "varchar(255)",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamptz(0)",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "timestamptz(0)",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"length": 6,
|
||||
"mappedType": "datetime"
|
||||
}
|
||||
},
|
||||
"name": "invites",
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"columnNames": [
|
||||
"invited_id"
|
||||
],
|
||||
"composite": false,
|
||||
"keyName": "invites_invited_id_unique",
|
||||
"primary": false,
|
||||
"unique": true
|
||||
},
|
||||
{
|
||||
"keyName": "invites_pkey",
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"composite": false,
|
||||
"primary": true,
|
||||
"unique": true
|
||||
}
|
||||
],
|
||||
"checks": [],
|
||||
"foreignKeys": {
|
||||
"invites_inviter_id_foreign": {
|
||||
"constraintName": "invites_inviter_id_foreign",
|
||||
"columnNames": [
|
||||
"inviter_id"
|
||||
],
|
||||
"localTableName": "public.invites",
|
||||
"referencedColumnNames": [
|
||||
"id"
|
||||
],
|
||||
"referencedTableName": "public.users",
|
||||
"deleteRule": "set null",
|
||||
"updateRule": "cascade"
|
||||
},
|
||||
"invites_invited_id_foreign": {
|
||||
"constraintName": "invites_invited_id_foreign",
|
||||
"columnNames": [
|
||||
"invited_id"
|
||||
],
|
||||
"localTableName": "public.invites",
|
||||
"referencedColumnNames": [
|
||||
"id"
|
||||
],
|
||||
"referencedTableName": "public.users",
|
||||
"deleteRule": "set null",
|
||||
"updateRule": "cascade"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"columns": {
|
||||
"id": {
|
||||
|
@ -546,6 +375,7 @@
|
|||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"default": "0",
|
||||
"mappedType": "integer"
|
||||
},
|
||||
"created_at": {
|
||||
|
@ -597,6 +427,127 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(255)",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "string"
|
||||
},
|
||||
"permissions": {
|
||||
"name": "permissions",
|
||||
"type": "int",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "integer"
|
||||
},
|
||||
"inviter_id": {
|
||||
"name": "inviter_id",
|
||||
"type": "varchar(255)",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "string"
|
||||
},
|
||||
"invited_id": {
|
||||
"name": "invited_id",
|
||||
"type": "varchar(255)",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamptz(0)",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"skip_verification": {
|
||||
"name": "skip_verification",
|
||||
"type": "boolean",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"default": "false",
|
||||
"mappedType": "boolean"
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "timestamptz(0)",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"length": 6,
|
||||
"mappedType": "datetime"
|
||||
}
|
||||
},
|
||||
"name": "invites",
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"columnNames": [
|
||||
"invited_id"
|
||||
],
|
||||
"composite": false,
|
||||
"keyName": "invites_invited_id_unique",
|
||||
"primary": false,
|
||||
"unique": true
|
||||
},
|
||||
{
|
||||
"keyName": "invites_pkey",
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"composite": false,
|
||||
"primary": true,
|
||||
"unique": true
|
||||
}
|
||||
],
|
||||
"checks": [],
|
||||
"foreignKeys": {
|
||||
"invites_inviter_id_foreign": {
|
||||
"constraintName": "invites_inviter_id_foreign",
|
||||
"columnNames": [
|
||||
"inviter_id"
|
||||
],
|
||||
"localTableName": "public.invites",
|
||||
"referencedColumnNames": [
|
||||
"id"
|
||||
],
|
||||
"referencedTableName": "public.users",
|
||||
"deleteRule": "set null",
|
||||
"updateRule": "cascade"
|
||||
},
|
||||
"invites_invited_id_foreign": {
|
||||
"constraintName": "invites_invited_id_foreign",
|
||||
"columnNames": [
|
||||
"invited_id"
|
||||
],
|
||||
"localTableName": "public.invites",
|
||||
"referencedColumnNames": [
|
||||
"id"
|
||||
],
|
||||
"referencedTableName": "public.users",
|
||||
"deleteRule": "set null",
|
||||
"updateRule": "cascade"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"columns": {
|
||||
"id": {
|
||||
|
@ -870,7 +821,68 @@
|
|||
"id"
|
||||
],
|
||||
"referencedTableName": "public.files",
|
||||
"deleteRule": "cascade",
|
||||
"deleteRule": "CASCADE",
|
||||
"updateRule": "cascade"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(255)",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "string"
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "varchar(255)",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "string"
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "timestamptz(0)",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"mappedType": "datetime"
|
||||
}
|
||||
},
|
||||
"name": "users_verification",
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"keyName": "users_verification_pkey",
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"composite": false,
|
||||
"primary": true,
|
||||
"unique": true
|
||||
}
|
||||
],
|
||||
"checks": [],
|
||||
"foreignKeys": {
|
||||
"users_verification_user_id_foreign": {
|
||||
"constraintName": "users_verification_user_id_foreign",
|
||||
"columnNames": [
|
||||
"user_id"
|
||||
],
|
||||
"localTableName": "public.users_verification",
|
||||
"referencedColumnNames": [
|
||||
"id"
|
||||
],
|
||||
"referencedTableName": "public.users",
|
||||
"deleteRule": "CASCADE",
|
||||
"updateRule": "cascade"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import { Migration } from '@mikro-orm/migrations';
|
||||
|
||||
export class Migration20230331131557 extends Migration {
|
||||
|
||||
async up(): Promise<void> {
|
||||
this.addSql('alter table "users" alter column "permissions" type int using ("permissions"::int);');
|
||||
this.addSql('alter table "users" alter column "permissions" set default 0;');
|
||||
|
||||
this.addSql('alter table "links" alter column "clicks" type int using ("clicks"::int);');
|
||||
this.addSql('alter table "links" alter column "clicks" set default 0;');
|
||||
|
||||
this.addSql('alter table "invites" add column "skip_verification" boolean not null default false;');
|
||||
}
|
||||
|
||||
async down(): Promise<void> {
|
||||
this.addSql('alter table "users" alter column "permissions" drop default;');
|
||||
this.addSql('alter table "users" alter column "permissions" type int using ("permissions"::int);');
|
||||
|
||||
this.addSql('alter table "invites" drop column "skip_verification";');
|
||||
|
||||
this.addSql('alter table "links" alter column "clicks" drop default;');
|
||||
this.addSql('alter table "links" alter column "clicks" type int using ("clicks"::int);');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import MikroOrmOptions from '../orm.js';
|
||||
import MikroOrmOptions from '../orm.config.js';
|
||||
import { MikroOrmModule } from '@mikro-orm/nestjs';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { GraphQLModule } from '@nestjs/graphql';
|
||||
|
@ -13,8 +13,8 @@ import { HostModule } from './host/host.module.js';
|
|||
import { InviteModule } from './invite/invite.module.js';
|
||||
import { PasteModule } from './paste/paste.module.js';
|
||||
import { StorageModule } from './storage/storage.module.js';
|
||||
import { ThumbnailModule } from './thumbnail/thumbnail.module.js';
|
||||
import { UserModule } from './user/user.module.js';
|
||||
import { ThumbnailModule } from './thumbnail/thumbnail.module.js';
|
||||
|
||||
@Module({
|
||||
providers: [AppResolver],
|
||||
|
@ -26,12 +26,6 @@ import { UserModule } from './user/user.module.js';
|
|||
sortSchema: true,
|
||||
allowBatchedQueries: true,
|
||||
autoSchemaFile: 'src/schema.gql',
|
||||
errorFormatter: (execution) => {
|
||||
return {
|
||||
statusCode: 200,
|
||||
response: execution,
|
||||
};
|
||||
},
|
||||
}),
|
||||
ScheduleModule.forRoot(),
|
||||
PassportModule,
|
||||
|
|
|
@ -5,8 +5,8 @@ import { JwtService } from '@nestjs/jwt';
|
|||
import bcrypt from 'bcryptjs';
|
||||
import crypto from 'crypto';
|
||||
import { authenticator } from 'otplib';
|
||||
import { User } from '../user/user.entity';
|
||||
import type { OTPEnabledDto } from './dto/otp-enabled.dto';
|
||||
import { User } from '../user/user.entity.js';
|
||||
import type { OTPEnabledDto } from './dto/otp-enabled.dto.js';
|
||||
|
||||
export enum TokenType {
|
||||
USER = 'USER',
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
OptionalProps,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
type IdentifiedReference,
|
||||
type Ref,
|
||||
} from '@mikro-orm/core';
|
||||
import { Field, ID, ObjectType } from '@nestjs/graphql';
|
||||
import { Exclude } from 'class-transformer';
|
||||
|
@ -51,18 +51,18 @@ export class File extends Resource {
|
|||
@Field({ nullable: true })
|
||||
name?: string;
|
||||
|
||||
@OneToOne({ entity: () => Thumbnail, nullable: true, eager: true, strategy: LoadStrategy.JOINED })
|
||||
@OneToOne({ entity: () => Thumbnail, nullable: true, eager: true, ref: true, strategy: LoadStrategy.JOINED })
|
||||
@Field(() => Thumbnail, { nullable: true })
|
||||
thumbnail?: Thumbnail;
|
||||
thumbnail?: Ref<Thumbnail>;
|
||||
|
||||
@Property()
|
||||
@Field()
|
||||
createdAt: Date = new Date();
|
||||
|
||||
@ManyToOne(() => User, { wrappedReference: true, hidden: true })
|
||||
@ManyToOne(() => User, { ref: true, hidden: true })
|
||||
@Exclude()
|
||||
@Index()
|
||||
owner: IdentifiedReference<User>;
|
||||
owner: Ref<User>;
|
||||
|
||||
getExtension() {
|
||||
return mimeType.extension(this.type) || 'bin';
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Selections } from '@jenyus-org/nestjs-graphql-utils';
|
||||
import { resolveSelections } from '@jenyus-org/graphql-utils';
|
||||
import { InjectRepository } from '@mikro-orm/nestjs';
|
||||
import { EntityRepository } from '@mikro-orm/postgresql';
|
||||
import { ForbiddenException, UseGuards } from '@nestjs/common';
|
||||
import { Args, ID, Mutation, Parent, Query, ResolveField, Resolver } from '@nestjs/graphql';
|
||||
import { Args, ID, Info, Mutation, Parent, Query, ResolveField, Resolver } from '@nestjs/graphql';
|
||||
import prettyBytes from 'pretty-bytes';
|
||||
import { ResourceLocations } from '../../types/resource-locations.type.js';
|
||||
import { UserId } from '../auth/auth.decorators.js';
|
||||
|
@ -15,11 +15,11 @@ export class FileResolver {
|
|||
|
||||
@Query(() => File)
|
||||
@UseGuards(OptionalJWTAuthGuard)
|
||||
async file(
|
||||
@Args('fileId', { type: () => ID }) fileId: string,
|
||||
@Selections([{ field: 'urls', selector: 'owner' }]) populate: any[]
|
||||
) {
|
||||
return this.fileRepo.findOneOrFail(fileId, { populate });
|
||||
async file(@Args('fileId', { type: () => ID }) fileId: string, @Info() info: any) {
|
||||
const populate = resolveSelections([{ field: 'urls', selector: 'owner' }], info) as any[];
|
||||
return this.fileRepo.findOneOrFail(fileId, {
|
||||
populate,
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => Boolean)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Entity, ManyToOne, OneToOne, OptionalProps, PrimaryKey, Property } from '@mikro-orm/core';
|
||||
import { Entity, ManyToOne, OneToOne, OptionalProps, PrimaryKey, Property, type Ref } from '@mikro-orm/core';
|
||||
import { Field, ID, ObjectType } from '@nestjs/graphql';
|
||||
import { config } from '../../config.js';
|
||||
import { generateDeleteKey } from '../../helpers/generate-delete-key.helper.js';
|
||||
|
@ -15,16 +15,20 @@ export class Invite {
|
|||
@Field({ nullable: true })
|
||||
permissions?: number;
|
||||
|
||||
@ManyToOne({ entity: () => User, nullable: true })
|
||||
inviter?: User;
|
||||
@ManyToOne({ entity: () => User, ref: true, nullable: true })
|
||||
inviter?: Ref<User>;
|
||||
|
||||
@OneToOne({ entity: () => User, nullable: true })
|
||||
invited?: User;
|
||||
@OneToOne({ entity: () => User, ref: true, nullable: true })
|
||||
invited?: Ref<User>;
|
||||
|
||||
@Property()
|
||||
@Field()
|
||||
createdAt: Date = new Date();
|
||||
|
||||
@Property()
|
||||
@Field()
|
||||
skipVerification: boolean = false;
|
||||
|
||||
@Property({ nullable: true })
|
||||
@Field({ nullable: true })
|
||||
expiresAt?: Date;
|
||||
|
@ -53,5 +57,5 @@ export class Invite {
|
|||
return `${config.rootHost.url}/${this.path}`;
|
||||
}
|
||||
|
||||
[OptionalProps]: 'url' | 'path' | 'consumed' | 'expired' | 'createdAt';
|
||||
[OptionalProps]: 'url' | 'path' | 'consumed' | 'expired' | 'createdAt' | 'skipVerification';
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { EntityRepository, MikroORM, UseRequestContext } from '@mikro-orm/core';
|
||||
import { EntityRepository, MikroORM, ref, UseRequestContext } from '@mikro-orm/core';
|
||||
import { InjectRepository } from '@mikro-orm/nestjs';
|
||||
import type { OnApplicationBootstrap } from '@nestjs/common';
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
@ -21,7 +21,7 @@ export class InviteService implements OnApplicationBootstrap {
|
|||
protected orm: MikroORM
|
||||
) {}
|
||||
|
||||
async create(inviterId: string | null, permissions: Permission | null) {
|
||||
async create(inviterId: string | null, permissions: Permission | null, extra?: Partial<Invite>) {
|
||||
const invite = this.inviteRepo.create({
|
||||
inviter: inviterId || undefined,
|
||||
permissions: permissions || undefined,
|
||||
|
@ -35,9 +35,14 @@ export class InviteService implements OnApplicationBootstrap {
|
|||
return this.inviteRepo.findOne(inviteId);
|
||||
}
|
||||
|
||||
async consume(invite: Invite) {
|
||||
this.inviteRepo.remove(invite);
|
||||
await this.inviteRepo.flush();
|
||||
async consume(invite: Invite, user: User) {
|
||||
invite.invited = ref(user);
|
||||
if (invite.skipVerification) {
|
||||
user.verifiedEmail = true;
|
||||
this.inviteRepo.persist(user);
|
||||
}
|
||||
|
||||
await this.inviteRepo.persistAndFlush(invite);
|
||||
}
|
||||
|
||||
@UseRequestContext()
|
||||
|
@ -50,7 +55,7 @@ export class InviteService implements OnApplicationBootstrap {
|
|||
return;
|
||||
}
|
||||
|
||||
const invite = await this.create(null, Permission.ADMINISTRATOR);
|
||||
const invite = await this.create(null, Permission.ADMINISTRATOR, { skipVerification: true });
|
||||
this.logger.log(`Go to ${invite.url} to create the first account.`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { Entity, type IdentifiedReference, ManyToOne, OptionalProps, PrimaryKey, Property } from '@mikro-orm/core';
|
||||
import { Entity, ManyToOne, OptionalProps, PrimaryKey, Property, type Ref } from '@mikro-orm/core';
|
||||
import { Field, ID, ObjectType } from '@nestjs/graphql';
|
||||
import { Exclude } from 'class-transformer';
|
||||
import { generateContentId } from '../../helpers/generate-content-id.helper.js';
|
||||
import { User } from '../user/user.entity.js';
|
||||
import { Resource } from '../../helpers/resource.entity-base.js';
|
||||
import { User } from '../user/user.entity.js';
|
||||
|
||||
@Entity({ tableName: 'links' })
|
||||
@ObjectType()
|
||||
|
@ -24,9 +24,9 @@ export class Link extends Resource {
|
|||
@Field()
|
||||
createdAt: Date = new Date();
|
||||
|
||||
@ManyToOne(() => User, { wrappedReference: true, hidden: true })
|
||||
@ManyToOne(() => User, { ref: true, hidden: true })
|
||||
@Exclude()
|
||||
owner: IdentifiedReference<User>;
|
||||
owner: Ref<User>;
|
||||
|
||||
getPaths() {
|
||||
return {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Entity, type IdentifiedReference, ManyToOne, OptionalProps, PrimaryKey, Property } from '@mikro-orm/core';
|
||||
import { Entity, ManyToOne, OptionalProps, PrimaryKey, Property, type Ref } from '@mikro-orm/core';
|
||||
import { Field, ID, InputType, ObjectType } from '@nestjs/graphql';
|
||||
import { Exclude } from 'class-transformer';
|
||||
import { IsBoolean, IsNumber, IsOptional, IsString, Length } from 'class-validator';
|
||||
|
@ -45,12 +45,8 @@ export class Paste extends Resource {
|
|||
createdAt: Date = new Date();
|
||||
|
||||
@Exclude()
|
||||
@ManyToOne(() => User, {
|
||||
hidden: true,
|
||||
nullable: true,
|
||||
wrappedReference: true,
|
||||
})
|
||||
owner?: IdentifiedReference<User>;
|
||||
@ManyToOne(() => User, { hidden: true, nullable: true, ref: true })
|
||||
owner?: Ref<User>;
|
||||
|
||||
@Field({ nullable: true })
|
||||
@Property({ persist: false })
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { HasFields, Selections } from '@jenyus-org/nestjs-graphql-utils';
|
||||
import { InjectRepository } from '@mikro-orm/nestjs';
|
||||
import { EntityRepository } from '@mikro-orm/postgresql';
|
||||
import { BadRequestException, UseGuards } from '@nestjs/common';
|
||||
import { Args, ID, Mutation, Parent, Query, ResolveField, Resolver } from '@nestjs/graphql';
|
||||
import { Args, ID, Info, Mutation, Parent, Query, ResolveField, Resolver } from '@nestjs/graphql';
|
||||
import { generateContentId, generateParanoidId } from '../../helpers/generate-content-id.helper.js';
|
||||
import { ResourceLocations } from '../../types/resource-locations.type.js';
|
||||
import { UserId } from '../auth/auth.decorators.js';
|
||||
|
@ -11,6 +10,7 @@ import { OptionalJWTAuthGuard } from '../auth/guards/optional-jwt.guard.js';
|
|||
import { HostService } from '../host/host.service.js';
|
||||
import { UserService } from '../user/user.service.js';
|
||||
import { CreatePasteDto, Paste } from './paste.entity.js';
|
||||
import { resolveSelections, hasFields } from '@jenyus-org/graphql-utils';
|
||||
|
||||
@Resolver(() => Paste)
|
||||
export class PasteResolver {
|
||||
|
@ -24,15 +24,16 @@ export class PasteResolver {
|
|||
@UseGuards(OptionalJWTAuthGuard)
|
||||
async paste(
|
||||
@UserId() userId: string,
|
||||
@HasFields('paste.content') wantsContent: boolean,
|
||||
@Args('pasteId', { type: () => ID }) pasteId: string,
|
||||
@Selections([{ field: 'urls', selector: 'owner' }, 'content']) populate?: any[]
|
||||
@Info() info: any,
|
||||
@Args('pasteId', { type: () => ID }) pasteId: string
|
||||
): Promise<Paste> {
|
||||
const populate = resolveSelections([{ field: 'urls', selector: 'owner' }, 'content'], info) as any[];
|
||||
const paste = await this.pasteRepo.findOneOrFail(pasteId, { populate });
|
||||
|
||||
// if the owner is viewing the paste, don't burn it
|
||||
// otherwise, set that bitch alight
|
||||
const isOwner = paste.owner?.id === userId;
|
||||
const wantsContent = hasFields(info, 'paste.content');
|
||||
if (paste.burn && !isOwner && wantsContent) {
|
||||
await this.pasteRepo.removeAndFlush(paste);
|
||||
paste.burnt = true;
|
||||
|
|
|
@ -3,7 +3,7 @@ import crypto from 'crypto';
|
|||
import fs from 'fs';
|
||||
import { nanoid } from 'nanoid';
|
||||
import path from 'path';
|
||||
import { ExifTransformer } from 'src/classes/ExifTransformer.js';
|
||||
import { ExifTransformer } from '../../classes/ExifTransformer.js';
|
||||
import { default as getSizeTransform } from 'stream-size';
|
||||
import { pipeline } from 'stream/promises';
|
||||
import { config } from '../../config.js';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { BlobType, Entity, OneToOne, OptionalProps, PrimaryKeyType, Property } from '@mikro-orm/core';
|
||||
import { BlobType, Entity, OneToOne, OptionalProps, PrimaryKeyType, Property, type Ref } from '@mikro-orm/core';
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
import { File } from '../file/file.entity.js';
|
||||
|
||||
|
@ -28,8 +28,8 @@ export class Thumbnail {
|
|||
@Property({ type: BlobType, lazy: true, hidden: true })
|
||||
data: Buffer;
|
||||
|
||||
@OneToOne({ entity: () => File, primary: true, onDelete: 'CASCADE' })
|
||||
file: File;
|
||||
@OneToOne({ entity: () => File, primary: true, ref: true, onDelete: 'CASCADE' })
|
||||
file: Ref<File>;
|
||||
|
||||
@Property()
|
||||
@Field()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { IsEmail, IsLowercase, IsNotIn, IsOptional, IsString, MaxLength, MinLength } from 'class-validator';
|
||||
import blocklist from '../../../blocklist.json';
|
||||
import blocklist from '../../../blocklist.json' assert { type: 'json' };
|
||||
|
||||
@InputType()
|
||||
export class CreateUserDto {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { IsEmail } from 'class-validator';
|
||||
import { IsEmail, IsOptional } from 'class-validator';
|
||||
|
||||
@InputType()
|
||||
export class ResendVerificationEmailDto {
|
||||
@IsEmail()
|
||||
@IsOptional()
|
||||
@Field()
|
||||
email: string;
|
||||
email?: string;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core';
|
||||
import { Entity, ManyToOne, PrimaryKey, Property, type Ref } from '@mikro-orm/core';
|
||||
import { randomBytes } from 'crypto';
|
||||
import { User } from './user.entity.js';
|
||||
|
||||
|
@ -7,8 +7,8 @@ export class UserVerification {
|
|||
@PrimaryKey()
|
||||
id: string = randomBytes(16).toString('hex');
|
||||
|
||||
@ManyToOne(() => User, { onDelete: 'CASCADE' })
|
||||
user: User;
|
||||
@ManyToOne(() => User, { ref: true, onDelete: 'CASCADE' })
|
||||
user: Ref<User>;
|
||||
|
||||
@Property()
|
||||
expiresAt: Date;
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
OptionalProps,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
type Ref,
|
||||
} from '@mikro-orm/core';
|
||||
import { Field, ID, ObjectType } from '@nestjs/graphql';
|
||||
import { Exclude } from 'class-transformer';
|
||||
|
@ -48,8 +49,8 @@ export class User {
|
|||
@Index()
|
||||
secret: string;
|
||||
|
||||
@OneToOne({ nullable: true })
|
||||
invite?: Invite;
|
||||
@OneToOne({ entity: () => Invite, nullable: true, ref: true })
|
||||
invite?: Ref<Invite>;
|
||||
|
||||
@Property()
|
||||
@Field(() => [String])
|
||||
|
|
|
@ -114,7 +114,7 @@ export class UserService {
|
|||
const verifyUrl = `${config.rootHost.url}/api/user/${verification.user.id}/verify/${verification.id}`;
|
||||
const html = UserService.EMAIL_TEMPLATE({ verifyUrl });
|
||||
await sendMail({
|
||||
to: verification.user.email,
|
||||
to: user.email,
|
||||
subject: 'Verify your account | micro',
|
||||
html: html,
|
||||
});
|
||||
|
@ -137,14 +137,16 @@ export class UserService {
|
|||
otpEnabled: false,
|
||||
});
|
||||
|
||||
console.log(user);
|
||||
if (data.email) {
|
||||
console.log('check');
|
||||
await this.checkEmail(data.email);
|
||||
await this.sendVerificationEmail(user);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.inviteService.consume(invite);
|
||||
await this.userRepo.persistAndFlush(user);
|
||||
await this.inviteService.consume(invite, user);
|
||||
await this.userRepo.flush();
|
||||
return user;
|
||||
} catch (error) {
|
||||
if (error instanceof UniqueConstraintViolationException) {
|
||||
|
@ -171,7 +173,7 @@ export class UserService {
|
|||
throw new BadRequestException('Invalid or expired verification code');
|
||||
}
|
||||
|
||||
verification.user.verifiedEmail = true;
|
||||
verification.user.$.verifiedEmail = true;
|
||||
await this.userRepo.persistAndFlush(verification.user);
|
||||
await this.verificationRepo.nativeDelete({
|
||||
user: userId,
|
||||
|
@ -179,11 +181,13 @@ export class UserService {
|
|||
}
|
||||
|
||||
async checkEmail(email: string) {
|
||||
console.log('findOne');
|
||||
const existingByLowerEmail = await this.userRepo.findOne({
|
||||
email: {
|
||||
$ilike: email.toLowerCase(),
|
||||
},
|
||||
});
|
||||
console.log('findOne DONE');
|
||||
|
||||
if (existingByLowerEmail) {
|
||||
throw new ConflictException('Username or email already exists.');
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
||||
/* eslint-disable import/no-default-export */
|
||||
import { LoadStrategy } from '@mikro-orm/core';
|
||||
import { FlushMode } from '@mikro-orm/core';
|
||||
import type { MikroOrmModuleSyncOptions } from '@mikro-orm/nestjs';
|
||||
import { Logger, NotFoundException } from '@nestjs/common';
|
||||
import { dirname, join } from 'path';
|
||||
|
@ -15,23 +15,25 @@ import { Thumbnail } from './modules/thumbnail/thumbnail.entity.js';
|
|||
import { UserVerification } from './modules/user/user-verification.entity.js';
|
||||
import { User } from './modules/user/user.entity.js';
|
||||
|
||||
export const ormLogger = new Logger('MikroORM');
|
||||
export const migrationsTableName = 'mikro_orm_migrations';
|
||||
process.env.MIKRO_ORM_DYNAMIC_IMPORTS = 'true';
|
||||
|
||||
export const ORM_LOGGER = new Logger('MikroORM');
|
||||
export const MIGRATIONS_TABLE_NAME = 'mikro_orm_migrations';
|
||||
|
||||
export default {
|
||||
type: 'postgresql',
|
||||
entities: [FileMetadata, File, Thumbnail, User, UserVerification, Invite, Paste, Link],
|
||||
clientUrl: config.databaseUrl,
|
||||
debug: true,
|
||||
loadStrategy: LoadStrategy.JOINED,
|
||||
flushMode: FlushMode.COMMIT,
|
||||
logger: (message) => {
|
||||
ormLogger.debug(message);
|
||||
ORM_LOGGER.debug(message);
|
||||
},
|
||||
findOneOrFailHandler: () => {
|
||||
throw new NotFoundException();
|
||||
},
|
||||
migrations: {
|
||||
path: join(dirname(fileURLToPath(import.meta.url)), 'migrations'),
|
||||
tableName: migrationsTableName,
|
||||
tableName: MIGRATIONS_TABLE_NAME,
|
||||
},
|
||||
} as MikroOrmModuleSyncOptions;
|
|
@ -87,6 +87,7 @@ type Invite {
|
|||
id: ID!
|
||||
path: String!
|
||||
permissions: Float
|
||||
skipVerification: Boolean!
|
||||
url: String!
|
||||
}
|
||||
|
||||
|
|
|
@ -1,34 +1,31 @@
|
|||
{
|
||||
"include": ["src", "types"],
|
||||
"compilerOptions": {
|
||||
// https://www.npmjs.com/package/@tsconfig/node16
|
||||
// https://www.npmjs.com/package/@tsconfig/node18
|
||||
// https://github.com/sindresorhus/tsconfig/blob/main/tsconfig.json
|
||||
"module": "node16",
|
||||
"moduleResolution": "node16",
|
||||
"moduleDetection": "force",
|
||||
"target": "es2021", // node 16
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true,
|
||||
"lib": ["es2022"],
|
||||
"module": "nodenext",
|
||||
"target": "es2022",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"moduleResolution": "node16",
|
||||
"declaration": true,
|
||||
"pretty": true,
|
||||
"newLine": "lf",
|
||||
"stripInternal": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": false,
|
||||
"esModuleInterop": true,
|
||||
"noUnusedLocals": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noEmitOnError": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"importsNotUsedAsValues": "error",
|
||||
"skipLibCheck": true,
|
||||
"isolatedModules": true,
|
||||
"outDir": "dist",
|
||||
"noUncheckedIndexedAccess": false,
|
||||
"strictPropertyInitialization": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"lib": ["es2021", "dom"],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
// https://github.com/bevry/istextorbinary/issues/270
|
||||
|
|
|
@ -15,46 +15,46 @@
|
|||
"generate": "graphql-codegen --config codegen.yml"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.7.0",
|
||||
"@headlessui/react": "^1.7.3",
|
||||
"@apollo/client": "^3.7.10",
|
||||
"@headlessui/react": "^1.7.13",
|
||||
"@ryanke/pandora": "^0.0.9",
|
||||
"@tailwindcss/typography": "^0.5.7",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"classnames": "^2.3.2",
|
||||
"concurrently": "^7.4.0",
|
||||
"copy-to-clipboard": "^3.3.2",
|
||||
"dayjs": "^1.11.5",
|
||||
"deepmerge": "^4.2.2",
|
||||
"concurrently": "^8.0.1",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"dayjs": "^1.11.7",
|
||||
"deepmerge": "^4.3.1",
|
||||
"formik": "^2.2.9",
|
||||
"generate-avatar": "1.4.10",
|
||||
"graphql": "^16.6.0",
|
||||
"http-status-codes": "^2.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"nanoid": "^4.0.0",
|
||||
"next": "12.2.0",
|
||||
"postcss": "^8.4.17",
|
||||
"nanoid": "^4.0.2",
|
||||
"next": "13.2.4",
|
||||
"postcss": "^8.4.21",
|
||||
"prism-react-renderer": "^1.3.5",
|
||||
"qrcode.react": "^3.1.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-feather": "^2.0.9",
|
||||
"react-markdown": "^8.0.3",
|
||||
"react-markdown": "^8.0.6",
|
||||
"rehype-raw": "^6.1.1",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"swr": "^1.3.0",
|
||||
"tailwindcss": "^3.1.8",
|
||||
"yup": "^0.32.11"
|
||||
"swr": "^2.1.1",
|
||||
"tailwindcss": "^3.3.1",
|
||||
"yup": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/cli": "^2.13.5",
|
||||
"@graphql-codegen/typescript": "2.7.3",
|
||||
"@graphql-codegen/typescript-operations": "2.5.3",
|
||||
"@graphql-codegen/typescript-react-apollo": "3.3.3",
|
||||
"@graphql-codegen/cli": "^3.2.2",
|
||||
"@graphql-codegen/typescript": "3.0.2",
|
||||
"@graphql-codegen/typescript-operations": "3.0.2",
|
||||
"@graphql-codegen/typescript-react-apollo": "3.3.7",
|
||||
"@sylo-digital/scripts": "^1.0.12",
|
||||
"@types/lodash": "^4.14.186",
|
||||
"@types/node": "16",
|
||||
"@types/react": "^18.0.21",
|
||||
"prettier": "^2.7.1",
|
||||
"typescript": "^4.8.4"
|
||||
"@types/lodash": "^4.14.192",
|
||||
"@types/node": "^18.15.11",
|
||||
"@types/react": "^18.0.31",
|
||||
"prettier": "^2.8.7",
|
||||
"typescript": "^5.0.3"
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { Button, ButtonStyle, Container, useAsync, useOnClickOutside } from '@ryanke/pandora';
|
||||
import { Button, ButtonStyle, Container, useAsync, useOnClickOutside, useToasts } from '@ryanke/pandora';
|
||||
import classNames from 'classnames';
|
||||
import { Fragment, memo, useRef, useState } from 'react';
|
||||
import { Crop } from 'react-feather';
|
||||
|
@ -17,6 +17,7 @@ export const Header = memo(() => {
|
|||
const [showEmailInput, setShowEmailInput] = useState(false);
|
||||
const emailInputRef = useRef<HTMLDivElement>(null);
|
||||
const [email, setEmail] = useState('');
|
||||
const createToast = useToasts();
|
||||
const [resent, setResent] = useState(false);
|
||||
const classes = classNames(
|
||||
'relative z-20 flex items-center justify-between h-16 my-auto transition',
|
||||
|
@ -30,21 +31,33 @@ export const Header = memo(() => {
|
|||
|
||||
const [resendMutation] = useResendVerificationEmailMutation();
|
||||
const [resendVerification, sendingVerification] = useAsync(async () => {
|
||||
if (!user.data) return;
|
||||
if (resent || !user.data) return;
|
||||
if (!user.data.email && !email) {
|
||||
setShowEmailInput(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = !user.data.email && email ? { email } : null;
|
||||
await resendMutation({
|
||||
variables: {
|
||||
data: payload,
|
||||
},
|
||||
});
|
||||
try {
|
||||
await resendMutation({
|
||||
variables: {
|
||||
data: payload,
|
||||
},
|
||||
});
|
||||
|
||||
setShowEmailInput(false);
|
||||
setResent(true);
|
||||
setShowEmailInput(false);
|
||||
setResent(true);
|
||||
} catch (error: any) {
|
||||
if (error.message.includes('You can only') || error.message.includes('You have already')) {
|
||||
createToast({
|
||||
text: 'You have already requested a verification email. Please check your inbox, or try resend in 5 minutes.',
|
||||
error: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -54,8 +67,13 @@ export const Header = memo(() => {
|
|||
<Container>
|
||||
<span className="relative">
|
||||
You must verify your email before you can upload files.{' '}
|
||||
<button type="button" className="underline" onClick={resendVerification} disabled={sendingVerification}>
|
||||
{resent ? 'Verification email sent' : 'Resend verification email'}
|
||||
<button
|
||||
type="button"
|
||||
className={resent ? 'cursor-default' : 'underline'}
|
||||
onClick={resendVerification}
|
||||
disabled={sendingVerification || resent}
|
||||
>
|
||||
{resent ? `Verification email sent to ${user.data.email}!` : 'Resend verification email'}
|
||||
</button>
|
||||
{showEmailInput && (
|
||||
<div
|
||||
|
|
|
@ -5,10 +5,6 @@ export interface LinkProps extends HTMLAttributes<HTMLAnchorElement> {
|
|||
href: string;
|
||||
}
|
||||
|
||||
export const Link: FC<LinkProps> = ({ href, children, ...rest }) => {
|
||||
return (
|
||||
<NextLink href={href} passHref>
|
||||
<a {...rest}>{children}</a>
|
||||
</NextLink>
|
||||
);
|
||||
export const Link: FC<LinkProps> = ({ children, ...rest }) => {
|
||||
return <NextLink {...rest}>{children}</NextLink>;
|
||||
};
|
||||
|
|
|
@ -25,7 +25,7 @@ export const Markdown = memo<{ children: string; className?: string }>(({ childr
|
|||
return (
|
||||
<div className={classes}>
|
||||
<ReactMarkdown
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
// rehypePlugins={[rehypeRaw]}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
components={{
|
||||
pre({ children }) {
|
||||
|
|
|
@ -100,6 +100,7 @@ export type Invite = {
|
|||
id: Scalars['ID'];
|
||||
path: Scalars['String'];
|
||||
permissions?: Maybe<Scalars['Float']>;
|
||||
skipVerification: Scalars['Boolean'];
|
||||
url: Scalars['String'];
|
||||
};
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ export default function Invite() {
|
|||
return (
|
||||
<Container centerY>
|
||||
<Title>You're Invited</Title>
|
||||
<h1 className="text-4xl font-bold text-center md:hidden">Sign Up</h1>
|
||||
<h1 className="text-4xl font-bold text-center mb-6 md:hidden">Sign Up</h1>
|
||||
{expiresAt && (
|
||||
<p className="mt-2 mb-2 text-xs text-center text-gray-600 md:hidden">
|
||||
This invite will expire <Time date={expiresAt} />.
|
||||
|
|
6433
pnpm-lock.yaml
6433
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue