mirror of
https://github.com/Sonny93/my-links.git
synced 2025-12-08 14:43:24 +00:00
refactor: controllers and models to adapt them to the previous version of my-links
This commit is contained in:
@@ -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');
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ export default class UsersController {
|
||||
nickName,
|
||||
avatarUrl,
|
||||
token,
|
||||
providerType: 'google',
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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
9
app/validators/search.ts
Normal 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(),
|
||||
})
|
||||
);
|
||||
8
database/default_table_fields.ts
Normal file
8
database/default_table_fields.ts
Normal 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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
);
|
||||
|
||||
@@ -103,7 +103,7 @@ function ProfileDropdown() {
|
||||
width={22}
|
||||
referrerPolicy="no-referrer"
|
||||
/>
|
||||
{user!.nickName}
|
||||
{user!.fullname}
|
||||
</UserCard>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -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}>
|
||||
|
||||
3
inertia/types/app.d.ts
vendored
3
inertia/types/app.d.ts
vendored
@@ -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[];
|
||||
|
||||
Reference in New Issue
Block a user