diff --git a/README.md b/README.md index 264ff39..b3c2c60 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,7 @@ You can run the following commands with a **preceding space**, preventing the pa - `delete-user ` - `delete-all-users` - `change-password ` +- `add-file <OWNER_USERNAME>` ## Changelog diff --git a/src/server/admin-cli/admin.ts b/src/server/admin-cli/admin.ts index 380fe8a..5d8a825 100644 --- a/src/server/admin-cli/admin.ts +++ b/src/server/admin-cli/admin.ts @@ -4,7 +4,8 @@ import { deleteUser, listUsers, deleteAllUsers, - changePassword + changePassword, + addFile } from './operations.js'; import { setupEnvironment } from '../core/environment/environment.js'; import { createDatabase } from '../core/database/database.js'; @@ -27,7 +28,8 @@ const runAdminCli = async () => { 'delete-user', 'list-users', 'delete-all-users', - 'change-password' + 'change-password', + 'add-file' ].includes(mode) === false ) { console.log(mode); @@ -83,6 +85,20 @@ const runAdminCli = async () => { const newPasswordRaw = process.argv[4]; await changePassword(username, newPasswordRaw); } + + if (mode === 'add-file') { + if (!process.argv[3] || !process.argv[4] || !process.argv[5]) { + console.log( + 'File path, file name and owner must be specified after mode arg. Exiting.' + ); + process.exit(1); + } + const filePath = process.argv[3]; + const fileName = process.argv[4]; + const ownerUsername = process.argv[5]; + + await addFile(filePath, fileName, ownerUsername); + } }; runAdminCli().catch((err) => { diff --git a/src/server/admin-cli/operations.ts b/src/server/admin-cli/operations.ts index 90d4ac6..7b02d1e 100644 --- a/src/server/admin-cli/operations.ts +++ b/src/server/admin-cli/operations.ts @@ -1,7 +1,12 @@ import bcrypt from 'bcryptjs'; -import { User } from '../models/User.js'; +import fs from 'fs'; +import path from 'path'; +import { v4 as uuid } from 'uuid'; +import type { CreationAttributes } from 'sequelize'; +import { User } from '../models/User.js'; import type { IUser, UserRole } from '../../shared/types.js'; +import { MediaItem } from '../models/MediaItem.js'; const createUser = (username: string, role: UserRole, passwordRaw: string) => { const user: IUser = {} as IUser; @@ -70,4 +75,44 @@ const changePassword = async (username: string, newPasswordRaw: string) => { await User.update({ password: newPasswordHashed }, { where: { username } }); }; -export { createUser, deleteUser, listUsers, deleteAllUsers, changePassword }; +const addFile = async (url: string, name: string, ownerName: string) => { + const fsPath = path.resolve('data/uploads', url); + if (!fs.existsSync(fsPath)) { + throw new Error(`File ${fsPath} does not exist!`); + } + + const user = await User.findOne({ where: { username: ownerName } }); + + if (!user) { + throw new Error(`User ${ownerName} does not exist!`); + } + + const id = uuid(); + const newFileName = `${id}-${url}`; + const newFsPath = path.resolve('data/uploads', newFileName); + fs.renameSync(fsPath, newFsPath); + console.log(`Media file at '${fsPath}' was renamed to '${newFileName}'`); + + const newMediaItem: CreationAttributes<MediaItem> = { + id: uuid(), + type: 'file', + owner: user.id, + name, + url: newFileName, + settings: {} + }; + + const mediaItem = await MediaItem.create(newMediaItem); + console.log( + `'${mediaItem.name}' (${mediaItem.id}) was added with owner '${user.username}' (${user.id})` + ); +}; + +export { + createUser, + deleteUser, + listUsers, + deleteAllUsers, + changePassword, + addFile +};