refactor: controllers and models to adapt them to the previous version of my-links

This commit is contained in:
Sonny
2024-05-24 23:54:43 +02:00
committed by Sonny
parent 31b4f22772
commit b28499a69a
19 changed files with 113 additions and 85 deletions

View File

@@ -120,6 +120,7 @@ export default class CollectionsController {
async getCollectionsByAuthorId(authorId: User['id']) {
return await Collection.query()
.where('author_id', authorId)
.orderBy('created_at')
.preload('links');
}

View File

@@ -49,6 +49,7 @@ export default class UsersController {
nickName,
avatarUrl,
token,
providerType: 'google',
}
);

View File

@@ -10,7 +10,7 @@ export default class LogRequest {
!request.url().startsWith('/@react-refresh') &&
!request.url().includes('.ts')
) {
logger.info(`-> ${request.method()}: ${request.url()}`);
logger.info(`[${request.method()}]: ${request.url()}`);
}
await next();
}

View File

@@ -1,29 +1,27 @@
import { BaseModel, CamelCaseNamingStrategy, beforeCreate, column } from '@adonisjs/lucid/orm';
import {
BaseModel,
CamelCaseNamingStrategy,
column,
} from '@adonisjs/lucid/orm';
import { DateTime } from 'luxon';
import { v4 as uuidv4 } from 'uuid';
export default class AppBaseModel extends BaseModel {
static namingStrategy = new CamelCaseNamingStrategy();
static selfAssignPrimaryKey = true;
@column({ isPrimary: true })
declare id: string; // UUID
declare id: number;
@column.dateTime({
autoCreate: true,
serializeAs: 'createdAt',
serializeAs: 'created_at',
})
declare createdAt: DateTime;
declare created_at: DateTime;
@column.dateTime({
autoCreate: true,
autoUpdate: true,
serializeAs: 'updatedAt',
serializeAs: 'updated_at',
})
declare updatedAt: DateTime;
@beforeCreate()
static assignUuid(item: any) {
item.id = uuidv4();
}
declare updated_at: DateTime;
}

View File

@@ -16,10 +16,10 @@ export default class Collection extends AppBaseModel {
declare visibility: Visibility;
@column()
declare nextId: string;
declare next_id: number;
@column()
declare authorId: string;
declare authorId: number;
@belongsTo(() => User, { foreignKey: 'authorId' })
declare author: BelongsTo<typeof User>;

View File

@@ -9,7 +9,7 @@ export default class Link extends AppBaseModel {
declare name: string;
@column()
declare description: string;
declare description: string | null;
@column()
declare url: string;
@@ -18,13 +18,13 @@ export default class Link extends AppBaseModel {
declare favorite: boolean;
@column()
declare collectionId: string;
declare collectionId: number;
@belongsTo(() => Collection, { foreignKey: 'collectionId' })
declare collection: BelongsTo<typeof Collection>;
@column()
declare authorId: string;
declare authorId: number;
@belongsTo(() => User, { foreignKey: 'authorId' })
declare author: BelongsTo<typeof User>;

View File

@@ -1,7 +1,7 @@
import Collection from '#models/collection';
import Link from '#models/link';
import type { GoogleToken } from '@adonisjs/ally/types';
import { column, manyToMany } from '@adonisjs/lucid/orm';
import { column, computed, manyToMany } from '@adonisjs/lucid/orm';
import type { ManyToMany } from '@adonisjs/lucid/types/relations';
import AppBaseModel from './app_base_model.js';
@@ -12,20 +12,23 @@ export default class User extends AppBaseModel {
@column()
declare name: string;
@column({ serializeAs: 'nickName' })
@column()
declare nickName: string; // public username
@column({ serializeAs: 'avatarUrl' })
@column()
declare avatarUrl: string;
@column()
declare isAdmin: boolean;
@column({ serializeAs: null })
declare token: GoogleToken;
declare token?: GoogleToken;
@column({ serializeAs: null })
declare providerId: string;
declare providerId: number;
@column({ serializeAs: null })
declare providerType: 'google';
@manyToMany(() => Collection, {
relatedKey: 'authorId',
@@ -36,4 +39,9 @@ export default class User extends AppBaseModel {
relatedKey: 'authorId',
})
declare links: ManyToMany<typeof Link>;
@computed()
get fullname() {
return this.nickName || this.name;
}
}

View File

@@ -2,7 +2,7 @@ import vine, { SimpleMessagesProvider } from '@vinejs/vine';
import { Visibility } from '../enums/visibility.js';
const params = vine.object({
id: vine.string().trim(),
id: vine.number(),
});
export const createCollectionValidator = vine.compile(
@@ -10,7 +10,7 @@ export const createCollectionValidator = vine.compile(
name: vine.string().trim().minLength(1).maxLength(254),
description: vine.string().trim().maxLength(254).nullable(),
visibility: vine.enum(Visibility),
nextId: vine.string().optional(),
nextId: vine.number(),
})
);
@@ -19,7 +19,7 @@ export const updateCollectionValidator = vine.compile(
name: vine.string().trim().minLength(1).maxLength(254),
description: vine.string().trim().maxLength(254).nullable(),
visibility: vine.enum(Visibility),
nextId: vine.string().optional(),
nextId: vine.number(),
params,
})

View File

@@ -1,7 +1,7 @@
import vine from '@vinejs/vine';
const params = vine.object({
id: vine.string().trim(),
id: vine.number(),
});
export const createLinkValidator = vine.compile(
@@ -10,7 +10,7 @@ export const createLinkValidator = vine.compile(
description: vine.string().trim().maxLength(300).optional(),
url: vine.string().trim(),
favorite: vine.boolean(),
collectionId: vine.string().trim(),
collectionId: vine.number(),
})
);
@@ -20,7 +20,7 @@ export const updateLinkValidator = vine.compile(
description: vine.string().trim().maxLength(300).optional(),
url: vine.string().trim(),
favorite: vine.boolean(),
collectionId: vine.string().trim(),
collectionId: vine.number(),
params,
})
@@ -37,7 +37,7 @@ export const updateLinkFavoriteStatusValidator = vine.compile(
favorite: vine.boolean(),
params: vine.object({
id: vine.string().trim(),
id: vine.number(),
}),
})
);

9
app/validators/search.ts Normal file
View File

@@ -0,0 +1,9 @@
import vine from '@vinejs/vine';
export const searchValidator = vine.compile(
vine.object({
searchTerm: vine.string().trim().minLength(1).maxLength(254),
links: vine.boolean(),
collections: vine.boolean(),
})
);

View File

@@ -0,0 +1,8 @@
import { Knex } from 'knex';
export function defaultTableFields(table: Knex.CreateTableBuilder) {
table.increments('id', { primaryKey: true }).first().unique().notNullable();
table.timestamp('created_at').notNullable();
table.timestamp('updated_at').nullable();
}

View File

@@ -1,27 +1,26 @@
import { defaultTableFields } from '#database/default_table_fields';
import { BaseSchema } from '@adonisjs/lucid/schema';
export default class extends BaseSchema {
protected tableName = 'users';
export default class CreateUsersTable extends BaseSchema {
static tableName = 'users';
async up() {
this.schema.createTable(this.tableName, (table) => {
table.uuid('id').primary().unique().notNullable();
this.schema.createTableIfNotExists(CreateUsersTable.tableName, (table) => {
table.string('email', 254).notNullable().unique();
table.string('name', 254).notNullable();
table.string('nick_name', 254).notNullable();
table.string('nick_name', 254).nullable();
table.text('avatar_url').notNullable();
table.boolean('is_admin').defaultTo(0).notNullable();
table.json('token').notNullable();
table.json('token').nullable();
table.string('provider_id').notNullable();
table.enum('provider_type', ['google']).notNullable().defaultTo('google');
table.timestamp('created_at').notNullable();
table.timestamp('updated_at').nullable();
defaultTableFields(table);
});
}
async down() {
this.schema.dropTable(this.tableName);
this.schema.dropTable(CreateUsersTable.tableName);
}
}

View File

@@ -1,40 +1,44 @@
import { defaultTableFields } from '#database/default_table_fields';
import { BaseSchema } from '@adonisjs/lucid/schema';
import { Visibility } from '../../app/enums/visibility.js';
export default class extends BaseSchema {
protected tableName = 'collections';
export default class CreateCollectionTable extends BaseSchema {
static tableName = 'collections';
private visibilityEnumName = 'collection_visibility';
async up() {
this.schema.raw(`DROP TYPE IF EXISTS ${this.visibilityEnumName}`);
this.schema.createTable(this.tableName, (table) => {
table.uuid('id').primary().unique().notNullable();
this.schema.createTableIfNotExists(
CreateCollectionTable.tableName,
(table) => {
table.string('name', 254).notNullable();
table.string('description', 254);
table.string('description', 254).nullable();
table
.uuid('next_id')
.enum('visibility', Object.values(Visibility), {
useNative: true,
enumName: this.visibilityEnumName,
existingType: false,
})
.nullable()
.defaultTo(Visibility.PRIVATE);
table
.integer('next_id')
.references('id')
.inTable('collections')
.defaultTo(null);
table
.uuid('author_id')
.integer('author_id')
.references('id')
.inTable('users')
.onDelete('CASCADE');
table.enum('visibility', Object.values(Visibility), {
useNative: true,
enumName: this.visibilityEnumName,
existingType: false,
});
table.timestamp('created_at');
table.timestamp('updated_at');
});
defaultTableFields(table);
}
);
}
async down() {
this.schema.raw(`DROP TYPE IF EXISTS ${this.visibilityEnumName}`);
this.schema.dropTable(this.tableName);
this.schema.dropTable(CreateCollectionTable.tableName);
}
}

View File

@@ -1,33 +1,31 @@
import { defaultTableFields } from '#database/default_table_fields';
import { BaseSchema } from '@adonisjs/lucid/schema';
export default class extends BaseSchema {
protected tableName = 'links';
export default class CreateLinksTable extends BaseSchema {
static tableName = 'links';
async up() {
this.schema.createTable(this.tableName, (table) => {
table.uuid('id').primary().unique().notNullable();
this.schema.createTableIfNotExists(CreateLinksTable.tableName, (table) => {
table.string('name', 254).notNullable();
table.string('description', 254);
table.string('description', 254).nullable();
table.text('url').notNullable();
table.boolean('favorite').notNullable().defaultTo(0);
table
.uuid('collection_id')
.integer('collection_id')
.references('id')
.inTable('collections')
.onDelete('CASCADE');
table
.uuid('author_id')
.integer('author_id')
.references('id')
.inTable('users')
.onDelete('CASCADE');
table.timestamp('created_at');
table.timestamp('updated_at');
defaultTableFields(table);
});
}
async down() {
this.schema.dropTable(this.tableName);
this.schema.dropTable(CreateLinksTable.tableName);
}
}

View File

@@ -16,13 +16,14 @@ export default class extends BaseSeeder {
export function createRandomUser() {
return {
id: faker.string.uuid(),
id: faker.number.int(),
email: faker.internet.email(),
name: faker.internet.userName(),
nickName: faker.internet.displayName(),
avatarUrl: faker.image.avatar(),
isAdmin: false,
providerId: faker.string.uuid(),
providerId: faker.number.int(),
providerType: 'google' as const,
token: {} as GoogleToken,
};
}

View File

@@ -4,7 +4,7 @@ import useUser from '~/hooks/use_user';
export default function UserCard() {
const { user, isAuthenticated } = useUser();
const altImage = `${user?.nickName}'s avatar`;
const altImage = `${user?.fullname}'s avatar`;
return (
isAuthenticated && (
<Item className="disable-hover">
@@ -14,7 +14,7 @@ export default function UserCard() {
alt={altImage}
referrerPolicy="no-referrer"
/>
{user.nickName}
{user.fullname}
</Item>
)
);

View File

@@ -103,7 +103,7 @@ function ProfileDropdown() {
width={22}
referrerPolicy="no-referrer"
/>
{user!.nickName}
{user!.fullname}
</UserCard>
}
>

View File

@@ -1,7 +1,7 @@
import { Link } from '@inertiajs/react';
import { route } from '@izzyjs/route/client';
import { useTranslation } from 'react-i18next';
import { BsGear } from 'react-icons/bs';
import { PiGearLight } from 'react-icons/pi';
import Modal from '~/components/common/modal/modal';
import LangSelector from '~/components/lang_selector';
import ThemeSwitcher from '~/components/theme_switcher';
@@ -20,7 +20,7 @@ export default function ModalSettings({
return (
<>
<OpenItem onClick={open}>
<BsGear />
<PiGearLight />
{t('settings')}
</OpenItem>
<Modal title={t('settings')} opened={isShowing} close={close}>

View File

@@ -1,10 +1,11 @@
import type Collection from '#models/collection';
type User = {
id: string;
id: number;
email: string;
name: string;
nickName: string;
fullname: string;
avatarUrl: string;
isAdmin: boolean;
collections: Collection[];