-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16 from Huy-DNA/feat/ls-command
Feat/ls command
- Loading branch information
Showing
14 changed files
with
193 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { userService } from '~/services/users'; | ||
import { formatArg } from '../utils'; | ||
import type { CommandFunc } from './types'; | ||
import { fileService } from '~/services/files'; | ||
import { groupService } from '~/services/groups'; | ||
|
||
export const ls: CommandFunc = async function(...args) { | ||
// discard `ls` | ||
args.shift(); | ||
// discard first space | ||
args.shift(); | ||
|
||
if (args.length > 1) { | ||
return ['Expect an optional dirname as argument.']; | ||
} | ||
|
||
const dirname = args.length ? formatArg(args[0]) : '.'; | ||
const res = await fileService.getFolderContent(dirname); | ||
|
||
if (res.isOk()) { | ||
const files = res.unwrap(); | ||
const fileLines = []; | ||
for (const file of files) { | ||
const fileType = formatFileType(file.fileType as string); | ||
const filePermissionBits = formatPermissionBits(file.permission as unknown as string); | ||
const fileOwner = (await userService.getMetaOfUser(file.ownerId)).unwrap().name; | ||
const fileGroup = (await groupService.getMetaOfGroup(file.groupId)).unwrap().name; | ||
fileLines.push(`${fileType}${filePermissionBits} ${fileOwner} ${fileGroup} ${file.name}`); | ||
} | ||
return [ | ||
`total ${files.length}`, | ||
...fileLines, | ||
]; | ||
} | ||
|
||
return [ | ||
res.error()!.message, | ||
]; | ||
}; | ||
|
||
function formatFileType(fileType: string): string { | ||
switch (fileType) { | ||
case 'file': return '-'; | ||
case 'directory': return 'd'; | ||
case 'symlink': return 'l'; | ||
default: throw new Error('Unreachable'); | ||
} | ||
} | ||
|
||
function formatPermissionBits(permissionBits: string): string { | ||
const ownerRead = permissionBits[3]; | ||
const ownerWrite = permissionBits[4]; | ||
const ownerExecute = permissionBits[5]; | ||
const groupRead = permissionBits[6]; | ||
const groupWrite = permissionBits[7]; | ||
const groupExecute = permissionBits[8]; | ||
const otherRead = permissionBits[9]; | ||
const otherWrite = permissionBits[10]; | ||
const otherExecute = permissionBits[11]; | ||
return `${ownerRead ? 'r' : '-'}${ownerWrite ? 'w' : '-'}${ownerExecute ? 'x' : '-'}${groupRead ? 'r' : '-'}${groupWrite ? 'w' : '-'}${groupExecute ? 'x' : '-'}${otherRead ? 'r' : '-'}${otherWrite ? 'w' : '-'}${otherExecute ? 'x' : '-'}`; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,5 +5,6 @@ export enum Command { | |
HELP = 'help', | ||
CD = 'cd', | ||
SU = 'su', | ||
LS = 'ls', | ||
USERADD = 'useradd', | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import * as db from 'zapatos/db'; | ||
import { dbPool } from '~/db/connection'; | ||
import { VirtualPath } from '~/lib/path'; | ||
import { AccessType, canAccess, FileType, trimQuote } from '~/server/utils'; | ||
|
||
export enum FileLsErrorCode { | ||
INVALID_PARAM = 1000, | ||
NOT_ENOUGH_PRIVILEGE = 2000, | ||
FILE_NOT_FOUND = 3000, | ||
} | ||
|
||
export default defineEventHandler(async (event) => { | ||
const { name } = getQuery(event); | ||
if (typeof name !== 'string') { | ||
return { error: { code: FileLsErrorCode.INVALID_PARAM, message: 'Expect the "name" query param to be string' } }; | ||
} | ||
if (!event.context.auth) { | ||
return { error: { code: FileLsErrorCode.NOT_ENOUGH_PRIVILEGE, message: 'Should be logged in as a user with enough privilege' } }; | ||
} | ||
const filepath = VirtualPath.create(trimQuote(name)); | ||
try { | ||
const { permission_bits: filePermissionBits, owner_id: fileOwnerId, group_id: fileGroupId, file_type: fileType, created_at: createdAt, updated_at: updatedAt } = await db.selectExactlyOne('files', { name: filepath.toString() }).run(dbPool); | ||
if ( | ||
!canAccess( | ||
{ userId: event.context.auth.userId as number, groupId: event.context.auth.groupId as number }, | ||
{ fileType: FileType.UNKNOWN, ownerId: fileOwnerId, groupId: fileGroupId, permissionBits: filePermissionBits }, | ||
AccessType.EXECUTE, | ||
) | ||
) { | ||
return { error: { code: FileLsErrorCode.NOT_ENOUGH_PRIVILEGE, message: 'Should be logged in as a user with enough privilege' } }; | ||
} | ||
|
||
if (fileType === 'file') { | ||
return { ok: { message: 'Fetch file meta successfully', data: { files: [{ name: filepath.toString(), ownerId: fileOwnerId, groupId: fileGroupId, fileType: fileType, createdAt, updatedAt, permissionBits: filePermissionBits }] } } }; | ||
} | ||
|
||
const files = await db.select('files', { name: db.conditions.and(db.conditions.like(`${filepath.toString()}/%`), db.conditions.notLike(`${filepath.toString()}/%/%`)) }).run(dbPool); | ||
|
||
return { ok: { message: 'Fetch folder\'s content successfully', data: { files: files.map(({ permission_bits, updated_at, name, file_type, created_at, owner_id, group_id }) => ({ name, fileType: file_type, createdAt: created_at, ownerId: owner_id, groupId: group_id, permissionBits: permission_bits, updatedAt: updated_at })) } } }; | ||
} catch { | ||
return { error: { code: FileLsErrorCode.FILE_NOT_FOUND, message: 'File not found' } }; | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ export const enum AccessType { | |
} | ||
|
||
export const enum FileType { | ||
UNKNOWN, | ||
REGULAR_FILE, | ||
DIRECTORY, | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,24 @@ | ||
import type { Diagnostic, Result } from './types'; | ||
import { Err, Ok, type Diagnostic, type Result } from './types'; | ||
|
||
export interface GroupMeta { | ||
name: string; | ||
groupId: number; | ||
id: number; | ||
createdAt: Date; | ||
} | ||
|
||
export const groupService = { | ||
async getMetaOfGroup (id: number): Promise<Result<GroupMeta, Diagnostic>> { | ||
const res = await $fetch('/api/groups', { | ||
method: 'get', | ||
query: { | ||
id, | ||
}, | ||
}); | ||
if (res.error) { | ||
return new Err({ code: res.error.code, message: res.error.message }); | ||
} | ||
const { ok: { data } } = res; | ||
return new Ok({ name: data.name, id: data.id, createdAt: data.createdAt }); | ||
|
||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters