Skip to content

Commit

Permalink
various type fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
goto-bus-stop committed Aug 29, 2024
1 parent ce594ec commit 8362576
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 75 deletions.
31 changes: 22 additions & 9 deletions src/Source.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,34 @@ import { SourceNoImportError } from './errors/index.js';
* @typedef {import('./plugins/playlists.js').PlaylistItemDesc} PlaylistItemDesc
*/

/**
* @typedef {{
* sourceType: string,
* sourceID: string,
* sourceData: import('type-fest').JsonObject | null,
* artist: string,
* title: string,
* duration: number,
* thumbnail: string,
* }} SourceMedia
*/

/**
* @typedef {object} SourcePluginV1
* @prop {undefined|1} api
* @prop {(ids: string[]) => Promise<PlaylistItemDesc[]>} get
* @prop {(query: string, page: unknown, ...args: unknown[]) => Promise<PlaylistItemDesc[]>} search
* @prop {(ids: string[]) => Promise<SourceMedia[]>} get
* @prop {(query: string, page: unknown, ...args: unknown[]) => Promise<SourceMedia[]>} search
* @prop {(context: ImportContext, ...args: unknown[]) => Promise<unknown>} [import]
*
* @typedef {object} SourcePluginV2
* @prop {2} api
* @prop {(context: SourceContext, ids: string[]) => Promise<PlaylistItemDesc[]>} get
* @prop {(context: SourceContext, ids: string[]) => Promise<SourceMedia[]>} get
* @prop {(
* context: SourceContext,
* query: string,
* page: unknown,
* ...args: unknown[]
* ) => Promise<PlaylistItemDesc[]>} search
* ) => Promise<SourceMedia[]>} search
* @prop {(context: ImportContext, ...args: unknown[]) => Promise<unknown>} [import]
* @prop {(context: SourceContext, entry: PlaylistItemDesc) =>
* Promise<import('type-fest').JsonObject>} [play]
Expand Down Expand Up @@ -101,8 +113,9 @@ class Source {
* Media items can provide their own sourceType, too, so media sources can
* aggregate items from different source types.
*
* @param {Omit<PlaylistItemDesc, 'sourceType'>[]} items
* @returns {PlaylistItemDesc[]}
* @template T
* @param {T[]} items
* @returns {(T & { sourceType: string })[]}
*/
addSourceType(items) {
return items.map((item) => ({
Expand All @@ -116,7 +129,7 @@ class Source {
*
* @param {User} user
* @param {string} id
* @returns {Promise<PlaylistItemDesc?>}
* @returns {Promise<SourceMedia?>}
*/
getOne(user, id) {
return this.get(user, [id])
Expand All @@ -128,7 +141,7 @@ class Source {
*
* @param {User} user
* @param {string[]} ids
* @returns {Promise<PlaylistItemDesc[]>}
* @returns {Promise<SourceMedia[]>}
*/
async get(user, ids) {
let items;
Expand All @@ -150,7 +163,7 @@ class Source {
* @param {string} query
* @param {TPagination} [page]
* @param {unknown[]} args
* @returns {Promise<PlaylistItemDesc[]>}
* @returns {Promise<SourceMedia[]>}
*/
async search(user, query, page, ...args) {
let results;
Expand Down
2 changes: 1 addition & 1 deletion src/controllers/acl.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ async function createRole(req, res) {
const { permissions } = req.body;
const { acl } = req.uwave;

const role = await acl.createRole(name, permissions);
const role = await acl.createRole(name, /** @type {import('../schema.js').Permission[]} */ (permissions));

res.status(201);
return toItemResponse(role, {
Expand Down
10 changes: 1 addition & 9 deletions src/controllers/now.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,12 @@ function toInt(str) {
* @param {import('../Uwave.js').default} uw
*/
async function getOnlineUsers(uw) {
const { db } = uw;

const userIDs = /** @type {UserID[]} */ (await uw.redis.lrange('users', 0, -1));
if (userIDs.length === 0) {
return [];
}

const users = await db.selectFrom('users')
.where('id', 'in', userIDs)
.select(['id', 'username', 'slug', 'createdAt'])
.execute();
// TODO remove
users.forEach((user) => user.roles = [])

const users = await uw.users.getUsersByIds(userIDs);
return users.map(serializeUser);
}

Expand Down
36 changes: 17 additions & 19 deletions src/plugins/playlists.js
Original file line number Diff line number Diff line change
Expand Up @@ -552,25 +552,23 @@ class PlaylistsRepository {

if (unknownMediaIDs.length > 0) {
// @ts-expect-error TS2322
const unknownMedias = await this.#uw.source(sourceType)
.get(user, unknownMediaIDs);
const toInsert = unknownMedias.map((media) => /** @type {Media} */ ({
id: /** @type {MediaID} */ (randomUUID()),
sourceType: media.sourceType,
sourceID: media.sourceID,
sourceData: jsonb(media.sourceData),
artist: media.artist,
title: media.title,
duration: media.duration,
thumbnail: media.thumbnail,
}));
const inserted = await db.insertInto('media')
.values(toInsert)
.returningAll()
.execute();

for (const media of inserted) {
allMedias.set(`${media.sourceType}:${media.sourceID}`, media);
const unknownMedias = await this.#uw.source(sourceType).get(user, unknownMediaIDs);
for (const media of unknownMedias) {
const newMedia = await db.insertInto('media')
.values({
id: /** @type {MediaID} */ (randomUUID()),
sourceType: media.sourceType,
sourceID: media.sourceID,
sourceData: jsonb(media.sourceData),
artist: media.artist,
title: media.title,
duration: media.duration,
thumbnail: media.thumbnail,
})
.returningAll()
.executeTakeFirstOrThrow();

allMedias.set(`${media.sourceType}:${media.sourceID}`, newMedia);
}
}
});
Expand Down
103 changes: 68 additions & 35 deletions src/plugins/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,6 @@ const avatarColumn = (eb) => eb.fn.coalesce(
/** @type {import('kysely').RawBuilder<string>} */ (sql`concat('https://sigil.u-wave.net/', ${eb.ref('users.id')})`),
);

/** @type {import('kysely').SelectExpression<import('../schema.js').Database, 'users'>[]} */
const userSelection = [
'users.id',
'users.username',
'users.slug',
'users.activePlaylistID',
'users.pendingActivation',
'users.createdAt',
'users.updatedAt',
(eb) => avatarColumn(eb).as('avatar'),
(eb) => userRolesColumn(eb).as('roles'),
]

class UsersRepository {
#uw;

Expand All @@ -69,29 +56,41 @@ class UsersRepository {
limit = 50,
} = pagination;

let baseQuery = db.selectFrom('users');
let query = db.selectFrom('users')
.select([
'users.id',
'users.username',
'users.slug',
'users.activePlaylistID',
'users.pendingActivation',
'users.createdAt',
'users.updatedAt',
(eb) => avatarColumn(eb).as('avatar'),
(eb) => userRolesColumn(eb).as('roles'),
])
.offset(offset)
.limit(limit);
if (filter != null) {
baseQuery = baseQuery.where('username', 'like', filter);
query = query.where('username', 'like', `%${filter}%`);
}

const totalQuery = db.selectFrom('users')
.select((eb) => eb.fn.countAll().as('count'));

const filteredQuery = filter == null ? totalQuery : baseQuery.select((eb) => eb.fn.countAll().as('count'));
.select((eb) => eb.fn.countAll().as('count'))
.executeTakeFirstOrThrow();

const query = baseQuery
.offset(offset)
.limit(limit)
.select(userSelection);
const filteredQuery = filter == null ? totalQuery : db.selectFrom('users')
.select((eb) => eb.fn.countAll().as('count'))
.where('username', 'like', `%${filter}%`)
.executeTakeFirstOrThrow();

const [
users,
filtered,
total,
] = await Promise.all([
query.execute(),
filteredQuery.executeTakeFirstOrThrow(),
totalQuery.executeTakeFirstOrThrow(),
filteredQuery,
totalQuery,
]);

return new Page(users, {
Expand All @@ -112,19 +111,37 @@ class UsersRepository {
* @param {UserID} id
*/
async getUser(id) {
const [user] = await this.getUsersByIds([id]);
return user ?? null;
}

/**
* @param {UserID[]} ids
*/
async getUsersByIds(ids) {
const { db } = this.#uw;

const user = await db.selectFrom('users')
.where('id', '=', id)
.select(userSelection)
.executeTakeFirst();
const users = await db.selectFrom('users')
.where('id', 'in', ids)
.select([
'users.id',
'users.username',
'users.slug',
'users.activePlaylistID',
'users.pendingActivation',
'users.createdAt',
'users.updatedAt',
(eb) => avatarColumn(eb).as('avatar'),
(eb) => userRolesColumn(eb).as('roles'),
])
.execute();

if (user == null) {
return null;
for (const user of users) {
const roles = /** @type {string[]} */ (JSON.parse(/** @type {string} */ (/** @type {unknown} */ (user.roles))));
Object.assign(user, { roles });
}

const roles = /** @type {string[]} */ (JSON.parse(/** @type {string} */ (/** @type {unknown} */ (user.roles))));
return Object.assign(user, { roles });
return /** @type {import('type-fest').SetNonNullable<(typeof users)[0], 'roles'>[]} */ (users);
}

/**
Expand Down Expand Up @@ -156,7 +173,17 @@ class UsersRepository {
async localLogin({ email, password }) {
const user = await this.#uw.db.selectFrom('users')
.where('email', '=', email)
.select([...userSelection, 'password'])
.select([
'users.id',
'users.username',
'users.slug',
(eb) => avatarColumn(eb).as('avatar'),
'users.activePlaylistID',
'users.pendingActivation',
'users.createdAt',
'users.updatedAt',
'users.password',
])
.executeTakeFirst();
if (!user) {
throw new UserNotFoundError({ email });
Expand Down Expand Up @@ -218,7 +245,13 @@ class UsersRepository {
'authServices.service',
'authServices.serviceID',
'authServices.serviceAvatar',
...userSelection,
'users.id',
'users.username',
'users.slug',
'users.activePlaylistID',
'users.pendingActivation',
'users.createdAt',
'users.updatedAt',
])
.executeTakeFirst();

Expand All @@ -237,7 +270,7 @@ class UsersRepository {
id: /** @type {UserID} */ (randomUUID()),
username: username ? username.replace(/\s/g, '') : `${type}.${id}`,
slug: slugify(username),
pendingActivation: true, // type,
pendingActivation: true,
avatar,
})
.returningAll()
Expand Down
13 changes: 11 additions & 2 deletions src/utils/serialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,16 @@ export function serializePlaylistItem(model) {
}

/**
* @param {import('../schema.js').User} model
* @param {{
* id: import('../schema.js').UserID,
* username: string,
* slug: string,
* roles: string[],
* avatar: string | null,
* activePlaylistID?: string | null,
* createdAt: Date,
* updatedAt?: Date,
* }} model
*/
export function serializeUser(model) {
return {
Expand All @@ -75,6 +84,6 @@ export function serializeUser(model) {
activePlaylist: model.activePlaylistID,
createdAt: model.createdAt.toISOString(),
updatedAt: model.updatedAt?.toISOString(),
lastSeenAt: model.lastSeenAt?.toISOString(),
// lastSeenAt: model.lastSeenAt?.toISOString(),
};
}

0 comments on commit 8362576

Please sign in to comment.