Skip to content

Commit

Permalink
Merge pull request #128 from MyLife-Services/azure-deploy-prod
Browse files Browse the repository at this point in the history
Azure deploy prod
  • Loading branch information
Mookse authored Jan 8, 2024
2 parents b41ba63 + 4da3b93 commit 85b4b27
Show file tree
Hide file tree
Showing 11 changed files with 351 additions and 12 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package-lock.json
/in-progress/
.env*
.vscode/
.personal/
__*/ # ignore all folders starting with an underscore
.hintrc
.uploads
Expand Down
58 changes: 58 additions & 0 deletions WORKLOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Worklog @mookse

## Open Todos

- [ ] openAI Dev Forum
- [ ] Introduce MyLife in Community
- [ ] Put Maht in queue for people to test
- [ ] ma.gov sec state + taxes (too soon)
- health insurance exchange (margaret on it?)

## 20240104

- [x] avatar modularize functions #120
- [ ] create signup GPT Action
- Outreach
- [ ] Permenant.org
- [ ] Future of Life Institute

## 20240103

- Maht front-end improvements/fixes
- [x] make input a textarea on chat pages, or boost like openai does
- [x] make sure markdown is still being converted to html
- [x] signup page isn't showing after success

## 20240102

- [x] LinkedIn
- [x] assign paid .org (?choose) domain to azure
- humanremembranceproject.org
- mydigitallife.world
- [x] github cleanup
- [Alpha Hardening](https://github.com/orgs/MyLife-Services/projects/8/views/1)

## 20231226

### Todos-20231226

list of pathways to check
- close non-current category from right **success**
- change category from none **success**
- change category from unanswered **success**
- close current category from right with answerset **success**
- remove all categories last removed = current 1/2 **success**
- remove all categories last removed = NO current 1/2 **success**
- change category when answerset exists 1/2 **success**

problem - metadata doesn't seem to work any more, may need to go through files
in which case, might be best to use UPDATES concept, make a widget for it

worth following through on the idea of contributions? Yes, regardless, for now

also, when grabbing content from GPOT to put into contribution response array, only take last sentence (and only if has question mark?)
let gpt do the work - see how good summaries are, in other words, ask at the end of the session, and stop storing local responses
quite successful with 4 engine, checking 3.5

- gpt changes category on me
- number of questions requires continuance?
57 changes: 54 additions & 3 deletions inc/js/functions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,52 @@ async function about(ctx){
ctx.state.title = `About MyLife`
await ctx.render('about') // about
}
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
ctx.MyLife.registerCandidate(_registrationData)
ctx.status = 200
ctx.body = {
success: true,
message: 'Registration completed successfully.',
data: _registrationData,
}
return
}
async function avatarListing(ctx){
ctx.state.title = `Avatars for ${ ctx.state.member.memberName }`
ctx.state.avatars = ctx.state.member.avatars
Expand Down Expand Up @@ -84,15 +130,18 @@ async function members(ctx){
break
}
}
async function privacyPolicy(ctx){
ctx.state.title = `MyLife Privacy Policy`
ctx.state.subtitle = `Effective Date: 2024-01-01`
await ctx.render('privacy-policy') // privacy-policy
}
async function signup(ctx) {
const { email, humanName, avatarNickname } = ctx.request.body
const _signupPackage = {
'email': email,
'humanName': humanName,
'avatarNickname': avatarNickname,
}
// Basic Email Regex for validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
// validate session signup
if (ctx.session.signup)
ctx.throw(400, 'Invalid input', {
Expand All @@ -113,7 +162,7 @@ async function signup(ctx) {
})
}
// Validate email
if (!emailRegex.test(email))
if (!ctx.Globals.isValidEmail(email))
ctx.throw(400, 'Invalid input', {
success: false,
message: 'Invalid input: emailInput',
Expand Down Expand Up @@ -191,13 +240,15 @@ function mSetContributions(ctx){
/* exports */
export {
about,
api_register,
avatarListing,
category,
challenge,
chat,
contributions,
index,
members,
privacyPolicy,
signup,
upload,
_upload,
Expand Down
4 changes: 4 additions & 0 deletions inc/js/globals.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import EventEmitter from 'events'
import { Guid } from 'js-guid' // usage = Guid.newGuid().toString()
// define global constants
const guid_regex = /^[0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i // regex for GUID validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ // regex for email validation
// modular classes
class Globals extends EventEmitter {
constructor() {
Expand All @@ -16,6 +17,9 @@ class Globals extends EventEmitter {
extractSysName(_mbr_id){
return _mbr_id.split('|')[0]
}
isValidEmail(_email){
return emailRegex.test(_email)
}
isValidGUID(_str) {
return guid_regex.test(_str)
}
Expand Down
18 changes: 17 additions & 1 deletion inc/js/mylife-data-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,18 @@ class Dataservices {
// ask global data service (stored proc) for passphrase
return await this.datamanager.challengeAccess(_mbr_id,_passphrase)
}
async findRegistrationIdByEmail(_email){
/* pull record for email, returning id or new guid */
const _ = await this.getItems(
'registration',
undefined,
[{ name: '@email',
value: _email,
}],
'registration'
)
return _?.[0]?.id??Guid.newGuid().toString() // needed to separate out, was failing
}
/**
* Retrieves a specific avatar by its ID.
* @async
Expand Down Expand Up @@ -296,11 +308,15 @@ class Dataservices {
return await this.datamanager.pushItem(_data)
}
/**
* Registers a new candidate to MyLife membership
* Registers a new candidate to MyLife membership after finding record (or contriving Guid) in db
* @public
* @param {object} _candidate { 'email': string, 'humanName': string, 'avatarNickname': string }
*/
async registerCandidate(_candidate){
_candidate.mbr_id = this.#partitionId
_candidate.id = await this.findRegistrationIdByEmail(_candidate.email)
_candidate.being = 'registration'
_candidate.name = `${_candidate.email.split('@')[0]}-${_candidate.email.split('@')[1]}_${_candidate.id}`
return await this.datamanager.registerCandidate(_candidate)
}
}
Expand Down
4 changes: 2 additions & 2 deletions inc/js/mylife-datamanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class Datamanager {
this.#containers = {
contribution_responses: this.database.container(_config.contributions.container.id),
members: this.database.container(_config.db.container.id),
registrations: this.database.container(_config.registrations.container.id),
registration: this.database.container(_config.registration.container.id),
}
this.requestOptions = {
partitionKey: this.#partitionId,
Expand Down Expand Up @@ -110,7 +110,7 @@ class Datamanager {
* @param {object} _candidate { 'email': string, 'humanName': string, 'avatarNickname': string }
*/
async registerCandidate(_candidate){
const { resource: doc } = await this.#containers['registrations']
const { resource: doc } = await this.#containers['registration']
.items
.upsert(_candidate)
return doc
Expand Down
2 changes: 1 addition & 1 deletion inc/js/mylife-datasource-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class Config{
partitionId: _mbr_id,
}
}
this.registrations={
this.registration={
id: process.env.MYLIFE_DB_NAME,
container: {
id: process.env.MYLIFE_REGISTRATION_DB_CONTAINER_NAME,
Expand Down
54 changes: 51 additions & 3 deletions inc/js/routes.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,39 @@
// imports
import Router from 'koa-router'
import { about, avatarListing, category, challenge, chat, contributions, index, members, signup, upload, _upload } from './functions.mjs'
import {
about,
api_register,
avatarListing,
category,
challenge,
chat,
contributions,
index,
members,
privacyPolicy,
signup,
upload,
_upload
} from './functions.mjs'
// variables
const _Router = new Router()
const _memberRouter = new Router()
const _apiRouter = new Router()
// root routes
_Router.get('/', index)
_Router.get('/about', about)
_Router.get('/status', status)
_Router.get('/members', members) // todo: this should be simpler and more precise a conductor of the request to sub-elements
_Router.get('/members/:mid', members) // todo: dual purposed at moment, should be part of /login route or something akin
_Router.get('/privacy-policy', privacyPolicy)
_Router.get('/signup', status_signup)
_Router.post('/', chat)
_Router.post('/challenge', challenge)
_Router.post('/signup', signup)
// members routes
/* api webhook routes */
_apiRouter.use(_tokenValidate)
_apiRouter.post('/register', api_register)
/* member routes */
_memberRouter.use(_memberValidate)
_memberRouter.get('/:mid/contributions/', contributions)
_memberRouter.get('/:mid/contributions/:cid', contributions)
Expand All @@ -24,8 +43,10 @@ _memberRouter.post('/', chat)
_memberRouter.post('/upload', _upload)
_memberRouter.post('/:mid/contributions/:cid', contributions)
_memberRouter.get('/:mid/avatars', avatarListing)
// Mount the _memberRouter on the main router at the '/members' path
// Mount the subordinate routers along respective paths
_Router.use('/members', _memberRouter.routes(), _memberRouter.allowedMethods())
_Router.use('/api/v1', _apiRouter.routes(), _apiRouter.allowedMethods())
/* mondular functions */
/**
* Connects the routes to the router
* @param {object} _Menu Menu object
Expand Down Expand Up @@ -64,6 +85,33 @@ function status(ctx){ // currently returns reverse "locked" status, could send o
function status_signup(ctx){
ctx.body = ctx.session.signup
}
/**
* Validates api token
* @param {object} ctx Koa context object
* @param {function} next Koa next function
* @returns {function} Koa next function
*/
async function _tokenValidate(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(_token!==process.env.OPENAI_JWT_SECRET){
ctx.status = 401
ctx.body = { error: 'Authorization token failure' }
return
}
await next()
} catch (error) {
ctx.status = 401
ctx.body = { error: 'Unauthorized Access' }
return
}
}
// exports
export default function init(_Menu) {
connectRoutes(_Menu)
Expand Down
7 changes: 5 additions & 2 deletions inc/js/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,11 @@ async function mCreateConversation(_session){ // thread can be acted on by any a
}, _session.factory)
_conversation.name = mAssignName(_session.mbr_id, _conversation.threadId)
// print to CosmosDB
if(process.env?.MYLIFE_DB_ALLOW_SAVE??false)
_session.factory.dataservices.pushItem(_conversation.inspect(true))
if(process.env?.MYLIFE_DB_ALLOW_SAVE === 'true'){ // env vars always strings
_session.factory.dataservices.pushItem(
_conversation.inspect(true)
)
}
return _conversation
}
function mValidCtxObject(_ctx){
Expand Down
Loading

0 comments on commit 85b4b27

Please sign in to comment.