2025-06-25 20:51:29 -07:00
import { get_session , get_user , PRECHECK_TABLE , require_user } from '../../../../utils/prechecks.ts' ;
2025-06-24 16:17:00 -07:00
import { PASSWORD_ENTRIES , PASSWORD_ENTRY } from '../../../../models/password_entry.ts' ;
2025-06-24 15:40:30 -07:00
import { SESSIONS } from '../../../../models/session.ts' ;
2025-06-24 16:17:00 -07:00
import { USER , USERS } from '../../../../models/user.ts' ;
2025-06-24 15:40:30 -07:00
import parse_body from '../../../../utils/bodyparser.ts' ;
2025-06-27 17:54:04 -07:00
import * as CANNED_RESPONSES from '../../../../utils/canned_responses.ts' ;
2025-06-24 15:40:30 -07:00
2025-06-25 20:51:29 -07:00
export const PRECHECKS : PRECHECK_TABLE = { } ;
2025-06-24 15:40:30 -07:00
// GET /api/users/:id - Get single user
2025-06-25 20:51:29 -07:00
PRECHECKS . GET = [ get_session , get_user , require_user , ( _req : Request , meta : Record < string , any > ) : Response | undefined = > {
2025-06-27 17:54:04 -07:00
const user_is_self = ! ! meta . user && ! ! meta . params && meta . user . id === meta . params . user_id ;
const can_read_self = meta . user ? . permissions . includes ( 'self.read' ) ;
const can_read_others = meta . user ? . permissions ? . includes ( 'users.read' ) ;
2025-06-24 15:40:30 -07:00
2025-06-25 20:51:29 -07:00
const has_permission = can_read_others || ( can_read_self && user_is_self ) ;
if ( ! has_permission ) {
return CANNED_RESPONSES . permission_denied ( ) ;
}
} ] ;
2025-06-24 15:40:30 -07:00
export async function GET ( _req : Request , meta : Record < string , any > ) : Promise < Response > {
2025-06-27 17:54:04 -07:00
const user_id : string = meta . params ? . user_id ? . toLowerCase ( ) . trim ( ) ? ? '' ;
2025-06-24 16:17:00 -07:00
const user : USER | null = user_id . length === 49 ? await USERS . get ( user_id ) : null ; // lurid is 49 chars as we use them, eg: "also-play-flow-want-form-wide-thus-work-burn-same"
2025-06-24 15:40:30 -07:00
if ( ! user ) {
return Response . json ( {
error : {
message : ` Could not locate a user with id: " ${ user_id } " ` ,
cause : 'unknown_user'
}
} , {
status : 404
} ) ;
}
return Response . json ( user , {
status : 200
} ) ;
}
// PUT /api/users/:id - Update user
2025-06-27 17:54:04 -07:00
PRECHECKS . PUT = [ get_session , get_user , require_user , ( req : Request , meta : Record < string , any > ) : Response | undefined = > {
const user_is_self = ! ! meta . user && ! ! meta . params && meta . user . id === meta . params . user_id ;
const can_write_self = meta . user ? . permissions . includes ( 'self.write' ) ;
const can_write_others = meta . user ? . permissions ? . includes ( 'users.write' ) ;
const is_a_test_override = Deno . env . get ( 'DENO_ENV' ) === 'test' && ! ! req . headers . get ( 'x-test-override' ) ;
2025-06-24 15:40:30 -07:00
2025-06-27 17:54:04 -07:00
const has_permission = is_a_test_override || can_write_others || ( can_write_self && user_is_self ) ;
2025-06-25 20:51:29 -07:00
if ( ! has_permission ) {
return CANNED_RESPONSES . permission_denied ( ) ;
}
} ] ;
2025-06-27 17:54:04 -07:00
export async function PUT ( req : Request , meta : Record < string , any > ) : Promise < Response > {
2025-06-24 15:40:30 -07:00
const now = new Date ( ) . toISOString ( ) ;
2025-06-27 17:54:04 -07:00
const id : string = meta . params . user_id ? ? '' ;
2025-06-24 16:17:00 -07:00
const existing = await USERS . get ( id ) ;
2025-06-24 15:40:30 -07:00
if ( ! existing ) {
return Response . json ( {
error : {
message : 'User not found' ,
cause : 'unknown_user'
}
} , {
status : 404
} ) ;
}
try {
const body = await parse_body ( req ) ;
2025-06-27 17:54:04 -07:00
const updated : USER = {
2025-06-24 15:40:30 -07:00
. . . existing ,
2025-06-27 17:54:04 -07:00
. . . body ,
2025-06-24 15:40:30 -07:00
timestamps : {
created : existing.timestamps.created ,
updated : now
}
} ;
2025-06-27 17:54:04 -07:00
if ( Array . isArray ( body . permissions ) && body . permissions . join ( ':' ) !== existing . permissions . join ( ':' ) ) {
const user_can_write_others = meta . user . permissions . includes ( 'users.write' ) ;
const is_a_test_override = Deno . env . get ( 'DENO_ENV' ) === 'test' && req . headers . get ( 'x-test-override' ) ;
const is_allowed = user_can_write_others || is_a_test_override ;
if ( ! is_allowed ) {
return CANNED_RESPONSES . permission_denied ( ) ;
}
}
2025-06-24 16:17:00 -07:00
await USERS . update ( updated ) ;
2025-06-24 15:40:30 -07:00
return Response . json ( updated , {
status : 200
} ) ;
} catch ( err ) {
return Response . json ( {
error : {
message : ( err as Error ) ? . message ? ? 'Unknown error due to invalid user data.' ,
cause : ( err as Error ) ? . cause ? ? 'invalid_user_data'
}
} , {
status : 400
} ) ;
}
}
// DELETE /api/users/:id - Delete user
2025-06-25 20:51:29 -07:00
PRECHECKS . DELETE = [ get_session , get_user , require_user , ( _req : Request , meta : Record < string , any > ) : Response | undefined = > {
2025-06-27 17:54:04 -07:00
const user_is_self = ! ! meta . user && ! ! meta . params && meta . user . id === meta . params . user_id ;
const can_write_self = meta . user ? . permissions . includes ( 'self.write' ) ;
const can_write_others = meta . user ? . permissions ? . includes ( 'users.write' ) ;
2025-06-24 15:40:30 -07:00
2025-06-25 20:51:29 -07:00
const has_permission = can_write_others || ( can_write_self && user_is_self ) ;
if ( ! has_permission ) {
return CANNED_RESPONSES . permission_denied ( ) ;
}
} ] ;
2025-06-27 17:54:04 -07:00
export async function DELETE ( _req : Request , meta : Record < string , any > ) : Promise < Response > {
const user_id : string = meta . params . user_id ? ? '' ;
2025-06-24 15:40:30 -07:00
2025-06-24 16:17:00 -07:00
const user : USER | null = await USERS . get ( user_id ) ;
2025-06-24 15:40:30 -07:00
if ( ! user ) {
return Response . json ( {
error : {
message : 'Error deleting user.' ,
cause : 'unknown_user'
}
} , {
status : 404
} ) ;
}
2025-06-24 16:17:00 -07:00
const password_entry : PASSWORD_ENTRY | null = await PASSWORD_ENTRIES . get ( user_id ) ;
2025-06-24 15:40:30 -07:00
if ( password_entry ) {
2025-06-24 16:17:00 -07:00
await PASSWORD_ENTRIES . delete ( password_entry ) ;
2025-06-24 15:40:30 -07:00
}
const sessions = await SESSIONS . find ( {
user_id
} ) ;
for ( const session of sessions ) {
await SESSIONS . delete ( session ) ;
}
2025-06-24 16:17:00 -07:00
await USERS . delete ( user ) ;
2025-06-24 15:40:30 -07:00
return Response . json ( {
deleted : true
} , {
status : 200
} ) ;
}