Skip to content

Commit

Permalink
Merge pull request #161 from MyLife-Services/160-openai-gpt-bots
Browse files Browse the repository at this point in the history
Remove unused files and update YAML structures
  • Loading branch information
Mookse committed Jan 29, 2024
2 parents 95c25f8 + eff3112 commit b73c52e
Show file tree
Hide file tree
Showing 18 changed files with 656 additions and 497 deletions.
31 changes: 0 additions & 31 deletions .env-sample

This file was deleted.

4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ __*/ # ignore all folders starting with an underscore
.uploads
.tmp
.DS_Store
.code-workspace
.code-workspace
.postman
postman
4 changes: 0 additions & 4 deletions .postman/api

This file was deleted.

19 changes: 0 additions & 19 deletions .postman/api_2dfb42e3-2ab7-4059-8f68-f0d16dec17bd

This file was deleted.

488 changes: 166 additions & 322 deletions README.md

Large diffs are not rendered by default.

188 changes: 188 additions & 0 deletions inc/js/api-functions.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import chalk from "chalk"
/* variables */
const mBotSecrets = JSON.parse(process.env.OPENAI_JWT_SECRETS)
/* private modular functions */
async function _keyValidation(ctx){ // transforms ctx.state
if(ctx.params.mid === ':mid') ctx.params.mid = undefined
// ctx session alternatives to hitting DB every time? can try...
const _mbr_id = ctx.params.mid??ctx.request.body.memberKey
const _validated =
( // session validation shorthand
( ctx.session?.isAPIValidated??false )
&& _mbr_id?.length
&& ( _mbr_id===ctx.session?.APIMemberKey??false )
) || ( // initial full validation
(_mbr_id?.length)
&& process.env.MYLIFE_HOSTED_MBR_ID.includes(_mbr_id)
&& await ctx.MyLife.testPartitionKey(_mbr_id)
)
ctx.state.mbr_id = _mbr_id
ctx.state.assistantType = _tokenType(ctx)
ctx.state.isValidated = _validated
ctx.session.isAPIValidated = ctx.state.isValidated
ctx.session.APIMemberKey = ctx.state.mbr_id
}
function _tokenType(ctx){
const _token = ctx.state.token
const _assistantType = mBotSecrets?.[_token]??'personal-avatar'
return _assistantType
}
function _tokenValidation(_token){
return mBotSecrets?.[_token]?.length??false
}
/* public modular functions */
async function keyValidation(ctx){
await _keyValidation(ctx)
if(!ctx.state.isValidated){
ctx.status = 400 // Bad Request
ctx.body = {
success: false,
message: 'Invalid member.',
}
return
}
ctx.status = 200 // OK
if(ctx.method === 'HEAD') return
// @todo: determine how to instantiate avatar via Maht Factory--session? In any case, perhaps relegate to session
const _memberCore = await ctx.MyLife.datacore(ctx.state.mbr_id)
const { updates, interests, birth, birthDate, fullName, names, nickname } = _memberCore
const _birth = (Array.isArray(birth) && birth.length)
? birth[0]
: birth??{}
_birth.date = birthDate??_birth.date??''
_birth.place = _birth.place??''
const _memberCoreData = {
mbr_id: ctx.state.mbr_id,
updates: updates??'',
interests: interests??'',
birthDate: _birth.date,
birthPlace: _birth.place,
fullName: fullName??names?.[0]??'',
preferredName: nickname??names?.[0].split(' ')[0]??'',
}
ctx.body = {
success: true,
message: 'Valid member.',
data: _memberCoreData,
}
console.log(chalk.yellowBright(`keyValidation():${_memberCoreData.mbr_id}`), _memberCoreData.fullName)
return
}
async function register(ctx){
const _registrationData = ctx.request.body
const {
registrationInterests,
contact={}, // as to not elicit error destructuring
personalInterests,
additionalInfo
} = _registrationData
const {
avatarName,
humanName,
humanDateOfBirth,
email,
city,
state,
country,
} = contact
if (!humanName?.length || !email?.length){
ctx.status = 400 // Bad Request
ctx.body = {
success: false,
message: 'Missing required contact information: humanName and/or email are required.',
}
return
}
// Email validation
if (!ctx.Globals.isValidEmail(contact.email)) {
ctx.status = 400 // Bad Request
ctx.body = {
success: false,
message: 'Invalid email format.',
}
return
}
// throttle requests?
// write to cosmos db
_registrationData.email = email // required at root for select
const _ = ctx.MyLife.registerCandidate(_registrationData)
const { mbr_id, ..._return } = _registrationData // abstract out the mbr_id
ctx.status = 200
ctx.body = {
success: true,
message: 'Registration completed successfully.',
data: _return,
}
return
}
/**
* Functionality around story contributions and portrayals
* @param {Koa} ctx - Koa Context object
* @returns {Koa} Koa Context object
*/
async function story(ctx){
await _keyValidation(ctx) // sets ctx.state.mbr_id and more
const { assistantType, mbr_id } = ctx.state
const { storySummary } = ctx.request?.body??{}
if(!storySummary?.length){
ctx.status = 400 // Bad Request
ctx.body = {
success: false,
message: 'No story summary provided. Use `storySummary` field.',
}
return
}
// write to cosmos db
const _story = await ctx.MyLife.story(mbr_id, assistantType, storySummary) // @todo: remove await
console.log(chalk.yellowBright('story submitted:'), _story)
ctx.status = 200 // OK
ctx.body = {
success: true,
message: 'Story submitted successfully.',
story: _story,
}
return
}
/**
* Validates api token
* @modular
* @public
* @param {object} ctx Koa context object
* @param {function} next Koa next function
* @returns {function} Koa next function
*/
async function tokenValidation(ctx, next) {
try {
const authHeader = ctx.request.headers['authorization']
if(!authHeader){
ctx.status = 401
ctx.body = { error: 'Authorization header is missing' }
return
}
const _token = authHeader.split(' ')[1] // Bearer TOKEN_VALUE
if(!_tokenValidation(_token)){
ctx.status = 401
ctx.body = { error: 'Authorization token failure' }
return
}
ctx.state.token = _token // **note:** keep first, as it is used in _tokenType()
ctx.state.assistantType = _tokenType(ctx)
await next()
} catch (error) {
ctx.status = 401
const _error = {
name: error.name,
message: error.message,
stack: error.stack
}
ctx.body = { message: 'Unauthorized Access', error: _error }
return
}
}
/* exports */
export {
keyValidation,
register,
story,
tokenValidation,
}
35 changes: 35 additions & 0 deletions inc/js/core.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,10 @@ class MyLife extends Organization { // form=server
constructor(_Factory){ // no session presumed to exist
super(_Factory)
}
async datacore(_mbr_id){
if(!_mbr_id || _mbr_id===this.mbr_id) throw new Error('datacore cannot be accessed')
return await this.factory.datacore(_mbr_id)
}
/* public functions */
/**
* Server MyLife _Maht instantiation uses this function to populate the most current alerts in the modular factory memoryspace. Currently only applicable to system types, but since this is implemented at the `core.mjs` scope, we can account
Expand All @@ -299,6 +303,37 @@ class MyLife extends Organization { // form=server
async registerCandidate(_candidate){
return await this.factory.registerCandidate(_candidate)
}
/**
* Submits a story to MyLife via API. Unclear if can be dual-purposed for internal, or if internal still instantiates API context.
* @public
* @param {string} _mbr_id - Member id
* @param {string} _assistantType - String name of assistant type
* @param {string} _summary - String summary of story
* @returns {object} - The story document from Cosmos.
*/
async story(_mbr_id, _assistantType, storySummary){
const id = this.globals.newGuid
const _story = {
assistantType: _assistantType,
being: 'story',
form: _assistantType,
id,
mbr_id: _mbr_id,
name: `${_assistantType}-story_${_mbr_id}_${id}`,
summary: storySummary,
}
const _storyCosmos = await this.factory.story(_story)
return this.globals.stripCosmosFields(_storyCosmos)
}
/**
* Tests partition key for member
* @public
* @param {string} _mbr_id member id
* @returns {boolean} returns true if partition key is valid
*/
async testPartitionKey(_mbr_id){
return await this.factory.testPartitionKey(_mbr_id)
}
/* getters/setters */
/**
* Gets MyLife agent role, refers to server entity Maht/MyLife
Expand Down
48 changes: 0 additions & 48 deletions inc/js/functions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,53 +27,6 @@ async function alerts(ctx){
ctx.body = await ctx.state.MemberSession.alerts(ctx.request.body)
}
}
async function api_register(ctx){
const _registrationData = ctx.request.body
const {
registrationInterests,
contact={}, // as to not elicit error destructuring
personalInterests,
additionalInfo
} = _registrationData
const {
avatarName,
humanName,
humanDateOfBirth,
email,
city,
state,
country,
} = contact
if (!humanName?.length || !email?.length){
ctx.status = 400 // Bad Request
ctx.body = {
success: false,
message: 'Missing required contact information: humanName and/or email are required.',
}
return
}
// Email validation
if (!ctx.Globals.isValidEmail(contact.email)) {
ctx.status = 400 // Bad Request
ctx.body = {
success: false,
message: 'Invalid email format.',
}
return
}
// throttle requests?
// write to cosmos db
_registrationData.email = email // required at root for select
const _ = ctx.MyLife.registerCandidate(_registrationData)
const { mbr_id, ..._return } = _registrationData // abstract out the mbr_id
ctx.status = 200
ctx.body = {
success: true,
message: 'Registration completed successfully.',
data: _return,
}
return
}
async function avatarListing(ctx){
ctx.state.title = `Avatars for ${ ctx.state.member.memberName }`
ctx.state.avatars = []
Expand Down Expand Up @@ -278,7 +231,6 @@ export {
about,
activateBot,
alerts,
api_register,
avatarListing,
bots,
category,
Expand Down
3 changes: 3 additions & 0 deletions inc/js/globals.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ class Globals extends EventEmitter {
isValidGuid(_str='') {
return (typeof _str === 'string' && guid_regex.test(_str))
}
stripCosmosFields(_obj){
return Object.fromEntries(Object.entries(_obj).filter(([k, v]) => !k.startsWith('_')))
}
sysId(_mbr_id){
if(!typeof _mbr_id==='string' || !_mbr_id.length || !_mbr_id.includes('|'))
throw new Error('expected MyLife member id string')
Expand Down
Loading

0 comments on commit b73c52e

Please sign in to comment.