Thing Time 📦
This commit is contained in:
parent
78a5c4d26e
commit
342de0aeb0
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,6 +1,10 @@
|
||||
node_modules
|
||||
app/node_modules
|
||||
api/node_modules
|
||||
/node_modules
|
||||
|
||||
localhost-key.pem
|
||||
localhost.pem
|
||||
|
||||
.vscode
|
||||
.vscode
|
||||
|
||||
.env
|
2
api/.env.example
Normal file
2
api/.env.example
Normal file
@ -0,0 +1,2 @@
|
||||
PORT=3847
|
||||
API_KEY="key"
|
20
api/.eslintrc.js
Normal file
20
api/.eslintrc.js
Normal file
@ -0,0 +1,20 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
commonjs: true,
|
||||
es2021: true,
|
||||
node: true,
|
||||
},
|
||||
extends: [
|
||||
'xo',
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 12,
|
||||
},
|
||||
rules: {
|
||||
semi: 'off',
|
||||
'padded-blocks': 'off',
|
||||
'object-curly-spacing': [2, 'always'],
|
||||
'capitalized-comments': 'off',
|
||||
camelcase: 'off',
|
||||
},
|
||||
}
|
9
api/.forcepush
Normal file
9
api/.forcepush
Normal file
@ -0,0 +1,9 @@
|
||||
push
|
||||
push
|
||||
push
|
||||
push
|
||||
push
|
||||
push
|
||||
push
|
||||
push
|
||||
push
|
53
api/package.json
Normal file
53
api/package.json
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "thingtime-api",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"dev": "node --max-http-header-size=9999999 src/index.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lopugit/thingtime-api.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/lopugit/thingtime-api/issues"
|
||||
},
|
||||
"homepage": "https://github.com/lopugit/thingtime-api#readme",
|
||||
"dependencies": {
|
||||
"axios": "^0.24.0",
|
||||
"bcrypt": "^5.0.1",
|
||||
"body-parser": "^1.19.1",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^10.0.0",
|
||||
"express": "^4.17.2",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21",
|
||||
"luxon": "^2.3.0",
|
||||
"mongodb": "^4.3.0",
|
||||
"pug": "^3.0.2",
|
||||
"smarts": "1.0.261",
|
||||
"socket.io": "^4.5.0",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxtjs/eslint-config": "^8.0.0",
|
||||
"@prettier/plugin-pug": "^1.19.1",
|
||||
"chai": "^4.3.6",
|
||||
"eslint": "^8.6.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-config-xo": "^0.39.0",
|
||||
"eslint-plugin-jest": "^25.3.4",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-nuxt": "^3.1.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-unicorn": "^40.0.0",
|
||||
"eslint-plugin-vue": "^8.2.0",
|
||||
"mocha": "^9.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
}
|
3384
api/pnpm-lock.yaml
Normal file
3384
api/pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load Diff
122
api/src/index.js
Normal file
122
api/src/index.js
Normal file
@ -0,0 +1,122 @@
|
||||
// Catches all uncaught errors so process never dies
|
||||
process.on('uncaughtException', err => {
|
||||
console.log('Caught exception: ', err);
|
||||
});
|
||||
|
||||
import dotenv from 'dotenv'
|
||||
dotenv.config()
|
||||
import bodyParser from 'body-parser'
|
||||
import express from 'express'
|
||||
import cors from 'cors'
|
||||
import http from 'http'
|
||||
const app = express()
|
||||
const server = http.createServer(app);
|
||||
app.use(bodyParser.json())
|
||||
const port = process.env.PORT
|
||||
import axios from 'axios'
|
||||
import pug from 'pug'
|
||||
import { DateTime } from 'luxon'
|
||||
import bcrypt from 'bcrypt'
|
||||
const saltRounds = 10
|
||||
import { get } from 'lodash-es'
|
||||
import { default as s } from 'smarts'
|
||||
const smarts = s()
|
||||
import thingtime from 'thingtime'
|
||||
import { Server } from 'socket.io';
|
||||
const io = new Server(server, {
|
||||
cors: {
|
||||
origin: '*',
|
||||
methods: ['GET', 'POST'],
|
||||
},
|
||||
})
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
(async () => {
|
||||
|
||||
await thingtime.init()
|
||||
|
||||
// Express middleware
|
||||
app.use(cors({
|
||||
origin: '*',
|
||||
}))
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.status(200).send('Hello ThingTime World!')
|
||||
})
|
||||
|
||||
app.get('/v1/thing', async (req, res) => {
|
||||
|
||||
// console.log('req', req.query)
|
||||
|
||||
const { request } = req.query
|
||||
|
||||
if (request === 'get') {
|
||||
|
||||
const { uuid } = req.query
|
||||
|
||||
const thing = await thingtime.get(uuid)
|
||||
|
||||
res.status(200).send({
|
||||
thing: smarts.serialize(thing),
|
||||
})
|
||||
|
||||
} else {
|
||||
|
||||
let { thing } = req.query
|
||||
thing = smarts.parse(thing, { noFunctions: true })
|
||||
|
||||
if (!thing) {
|
||||
res.status(400).send('No thing provided')
|
||||
return
|
||||
}
|
||||
|
||||
await thingtime.save(thing)
|
||||
|
||||
res.status(200).send()
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
// socket config
|
||||
io.on('connection', socket => {
|
||||
console.log('Something with socket id', socket.id, 'connected')
|
||||
|
||||
socket.on('registerListener', msg => {
|
||||
console.log('Message from socket with id', socket.id)
|
||||
thingtime.registerListener(socket, msg.uuid)
|
||||
})
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
console.log('Something with socket id', socket.id, 'disconnected')
|
||||
})
|
||||
});
|
||||
|
||||
app.get('/privacy-policy', async (req, res) => res.status(200).send(pug.compile(`
|
||||
.privacy-policy(
|
||||
style="max-width: 600px margin: 0 auto padding-top: 100px"
|
||||
)
|
||||
h1.title.text-white(
|
||||
style='fontSize: 48px fontWeight: bold'
|
||||
)
|
||||
| Privacy Policy
|
||||
p.subtitle
|
||||
| The friendly ThingTime API Privacy Policy
|
||||
p.answer
|
||||
.pt-12 This API uses YouTube API Services
|
||||
.pt-12 ThingTime API does not use any analytics tools to store any data, nor does it store any user data of any kind.
|
||||
.pt-12 We do not allow any 3rd parties to serve Ads on ThingTime API
|
||||
.pt-12 You can contact ThingTime API at
|
||||
a(href='emailto:subberAPI@alopu.com', style="padding-left: 6px") subberAPI@alopu.com
|
||||
.pt-12
|
||||
a.underline(href='https://www.youtube.com/t/terms') YouTube Terms of Service
|
||||
.pt-12
|
||||
a.underline(href='https://policies.google.com/privacy') Google Privacy Policy
|
||||
style(type="text/css").
|
||||
.pt-12 { padding-top: 12px }
|
||||
`)()))
|
||||
|
||||
server.listen(port, () => {
|
||||
console.log(`Example app listening at http://localhost:${port}`)
|
||||
})
|
||||
})()
|
15
api/src/node_modules/extractAllDependancies/index.js
generated
vendored
Normal file
15
api/src/node_modules/extractAllDependancies/index.js
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
import extractDependancies from 'extractDependancies'
|
||||
|
||||
const extractAllDependancies = function(objects) {
|
||||
|
||||
const dependancies = []
|
||||
|
||||
for (const object of objects) {
|
||||
extractDependancies(object, dependancies)
|
||||
}
|
||||
|
||||
return dependancies
|
||||
|
||||
}
|
||||
|
||||
export { extractAllDependancies as default, extractAllDependancies }
|
12
api/src/node_modules/extractAllDependancies/package.json
generated
vendored
Normal file
12
api/src/node_modules/extractAllDependancies/package.json
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "extractObjects",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
28
api/src/node_modules/extractDependancies/index.js
generated
vendored
Normal file
28
api/src/node_modules/extractDependancies/index.js
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
const extractDependancies = function(object, dependancies = []) {
|
||||
|
||||
walkObjectForDependancies(object, dependancies)
|
||||
|
||||
return dependancies
|
||||
|
||||
}
|
||||
|
||||
const walkObjectForDependancies = function(object, dependancies = [], seen = []) {
|
||||
|
||||
for (const key in object) {
|
||||
if (object.hasOwnProperty(key)) {
|
||||
const value = object[key]
|
||||
if (value && typeof value === 'object') {
|
||||
if (value.uuid && !dependancies.includes(value.uuid)) {
|
||||
dependancies.push(value.uuid)
|
||||
}
|
||||
if (!seen.includes(value)) {
|
||||
seen.push(value)
|
||||
walkObjectForDependancies(value, dependancies, seen)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { extractDependancies as default, extractDependancies }
|
12
api/src/node_modules/extractDependancies/package.json
generated
vendored
Normal file
12
api/src/node_modules/extractDependancies/package.json
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "extractObjects",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
34
api/src/node_modules/extractDependantPaths/index.js
generated
vendored
Normal file
34
api/src/node_modules/extractDependantPaths/index.js
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
const extractDependantPaths = function(dependantObject, objectUuid) {
|
||||
|
||||
const dependantPaths = []
|
||||
|
||||
walkObjectForDependantPaths(dependantObject, dependantPaths, objectUuid)
|
||||
|
||||
return dependantPaths
|
||||
|
||||
}
|
||||
|
||||
const walkObjectForDependantPaths = function(dependantObject, dependantPaths = [], objectUuid, currentPath = "", seen = []) {
|
||||
|
||||
for (const key in dependantObject) {
|
||||
const newPath = currentPath ? currentPath + '.' + key : key
|
||||
if (dependantObject.hasOwnProperty(key)) {
|
||||
const value = dependantObject[key]
|
||||
if (value && typeof value === 'object') {
|
||||
if (
|
||||
(value.uuid && value.uuid === objectUuid) &&
|
||||
!dependantPaths.includes(newPath)
|
||||
) {
|
||||
dependantPaths.push(newPath)
|
||||
}
|
||||
if (!seen.includes(value)) {
|
||||
seen.push(value)
|
||||
walkObjectForDependantPaths(value, dependantPaths, objectUuid, newPath, seen)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { extractDependantPaths as default, extractDependantPaths }
|
12
api/src/node_modules/extractDependantPaths/package.json
generated
vendored
Normal file
12
api/src/node_modules/extractDependantPaths/package.json
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "extractObjects",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
63
api/src/node_modules/extractObjects/index.js
generated
vendored
Normal file
63
api/src/node_modules/extractObjects/index.js
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
const extractObjects = function(thing, seen = [], things = []) {
|
||||
|
||||
things.push(thing)
|
||||
|
||||
if (['object', 'function'].includes(typeof thing) && thing.uuid) {
|
||||
const keys = Object.keys(thing)
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i]
|
||||
if (key !== 'thingtime') {
|
||||
const value = thing[key]
|
||||
if (['object', 'function'].includes(typeof value) && value.uuid && !seen.includes(value)) {
|
||||
// const saveable = toSaveable(value)
|
||||
// things.push(saveable)
|
||||
seen.push(value)
|
||||
extractObjects(value, seen, things)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return things
|
||||
|
||||
};
|
||||
|
||||
function toSaveable(thing) {
|
||||
let clone
|
||||
if (thing instanceof Object) {
|
||||
clone = {}
|
||||
const keys = Object.keys(thing)
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i]
|
||||
const value = thing[key]
|
||||
if (key !== 'thingtime' && ['object', 'array'].includes(typeof value) && value.uuid) {
|
||||
clone[key] = {
|
||||
_id: value.uuid
|
||||
}
|
||||
} else if(!['object', 'array'].includes(typeof value)) {
|
||||
clone[key] = value
|
||||
}
|
||||
}
|
||||
} else if (thing instanceof Array) {
|
||||
clone = {
|
||||
array: [],
|
||||
_id: thing.uuid,
|
||||
}
|
||||
for (let i = 0; i < thing.length; i++) {
|
||||
const value = thing[i]
|
||||
if (['object', 'array'].includes(typeof value) && value.uuid) {
|
||||
clone.array.push(value.uuid)
|
||||
} else {
|
||||
clone.array.push(value)
|
||||
}
|
||||
}
|
||||
} else if (thing instanceof Function) {
|
||||
clone = {
|
||||
function: thing.toString(),
|
||||
_id: thing.uuid
|
||||
}
|
||||
}
|
||||
return clone
|
||||
}
|
||||
|
||||
export { extractObjects as default, extractObjects }
|
12
api/src/node_modules/extractObjects/package.json
generated
vendored
Normal file
12
api/src/node_modules/extractObjects/package.json
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "extractObjects",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
13
api/src/node_modules/extractUuids/index.js
generated
vendored
Normal file
13
api/src/node_modules/extractUuids/index.js
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
const extractUuids = function(objects) {
|
||||
|
||||
const uuids = []
|
||||
|
||||
for (let object of objects) {
|
||||
uuids.push(object.uuid)
|
||||
}
|
||||
|
||||
return uuids
|
||||
|
||||
};
|
||||
|
||||
export { extractUuids as default, extractUuids }
|
12
api/src/node_modules/extractUuids/package.json
generated
vendored
Normal file
12
api/src/node_modules/extractUuids/package.json
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "extractObjects",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
15
api/src/node_modules/mapObjects/index.js
generated
vendored
Normal file
15
api/src/node_modules/mapObjects/index.js
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
const mapObjects = function(objects) {
|
||||
|
||||
const map = {}
|
||||
|
||||
for (const object of objects) {
|
||||
if (object.uuid) {
|
||||
map[object.uuid] = object
|
||||
}
|
||||
}
|
||||
|
||||
return map
|
||||
|
||||
}
|
||||
|
||||
export { mapObjects as default, mapObjects }
|
12
api/src/node_modules/mapObjects/package.json
generated
vendored
Normal file
12
api/src/node_modules/mapObjects/package.json
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "extractObjects",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
25
api/src/node_modules/test/app.js
generated
vendored
Normal file
25
api/src/node_modules/test/app.js
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
const apper = function(){
|
||||
|
||||
const app = {}
|
||||
|
||||
app.components = {
|
||||
root: {
|
||||
template: `<div>Hello World!</div>`,
|
||||
},
|
||||
}
|
||||
app.datas = {
|
||||
obj: {
|
||||
string: 'Hello World!',
|
||||
function: function(){ return 'Hello world!' },
|
||||
number: 123,
|
||||
boolean: true,
|
||||
array: [1, 2, 3],
|
||||
},
|
||||
}
|
||||
|
||||
return app
|
||||
|
||||
}
|
||||
|
||||
export { apper as default, apper}
|
3
api/src/node_modules/test/index.js
generated
vendored
Normal file
3
api/src/node_modules/test/index.js
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
import app from './app.js'
|
||||
|
||||
export { app as default, app }
|
12
api/src/node_modules/test/package.json
generated
vendored
Normal file
12
api/src/node_modules/test/package.json
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "test",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
104
api/src/node_modules/tests/index.js
generated
vendored
Normal file
104
api/src/node_modules/tests/index.js
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
const test = it
|
||||
import { expect } from 'chai'
|
||||
import apper from 'test'
|
||||
import extractObjects from 'extractObjects'
|
||||
import { thing } from 'thing'
|
||||
import smartsSource from 'smarts'
|
||||
import axios from 'axios'
|
||||
import dotenv from 'dotenv'
|
||||
dotenv.config()
|
||||
|
||||
const smarts = smartsSource()
|
||||
|
||||
const apiUrl = 'http://localhost:' + process.env.PORT
|
||||
|
||||
export default function () {
|
||||
|
||||
describe("Test object extraction", () => {
|
||||
test('Correct number of objects should be extracted', () => {
|
||||
|
||||
const app = apper()
|
||||
|
||||
thing(app)
|
||||
|
||||
const extracted = extractObjects(app)
|
||||
|
||||
expect(extracted?.length).to.equal(7)
|
||||
})
|
||||
})
|
||||
|
||||
describe("Test array serialization", () => {
|
||||
test('Correct number of objects should be extracted', () => {
|
||||
|
||||
const app = {
|
||||
array: [1,2,3]
|
||||
}
|
||||
thing(app)
|
||||
|
||||
const serialized = smarts.serialize(app)
|
||||
const loaded = smarts.load(serialized)
|
||||
|
||||
expect(loaded.array instanceof Array).to.equal(true)
|
||||
expect(typeof loaded.array.uuid).to.equal('string')
|
||||
})
|
||||
})
|
||||
|
||||
describe("Test object serialization", () => {
|
||||
test('Function should retain uuid', () => {
|
||||
const app = apper()
|
||||
|
||||
thing(app)
|
||||
|
||||
const serialized = smarts.serialize(app)
|
||||
const loaded = smarts.load(serialized)
|
||||
|
||||
expect(loaded.datas.obj.function.uuid).to.equal(app.datas.obj.function.uuid)
|
||||
})
|
||||
})
|
||||
describe("Test object saving", async () => {
|
||||
test('Save an app-like object', async () => {
|
||||
const app = apper()
|
||||
|
||||
thing(app)
|
||||
|
||||
const serialized = smarts.serialize(app)
|
||||
|
||||
const resp = await axios.get(apiUrl + '/v1/thing', {
|
||||
params: {
|
||||
thing: serialized,
|
||||
}
|
||||
}).catch(console.error)
|
||||
|
||||
expect(resp && resp.data).to.exist
|
||||
})
|
||||
})
|
||||
|
||||
describe("Test dependancy updating", async () => {
|
||||
test('Save an app-like object', async () => {
|
||||
const app = apper()
|
||||
|
||||
thing(app)
|
||||
|
||||
const serialized1 = smarts.serialize(app)
|
||||
|
||||
const resp1 = await axios.get(apiUrl + '/v1/thing', {
|
||||
params: {
|
||||
thing: serialized1,
|
||||
}
|
||||
}).catch(console.error)
|
||||
|
||||
app.datas.newVal = 'foo'
|
||||
|
||||
const serialized2 = smarts.serialize(app.datas)
|
||||
|
||||
const resp2 = await axios.get(apiUrl + '/v1/thing', {
|
||||
params: {
|
||||
thing: serialized2,
|
||||
}
|
||||
}).catch(console.error)
|
||||
|
||||
expect(resp2 && resp2.data).to.exist
|
||||
})
|
||||
})
|
||||
|
||||
}
|
12
api/src/node_modules/tests/package.json
generated
vendored
Normal file
12
api/src/node_modules/tests/package.json
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "tests",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
3
api/src/node_modules/thing/index.js
generated
vendored
Normal file
3
api/src/node_modules/thing/index.js
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
import thing from './thing.js'
|
||||
|
||||
export { thing as default, thing }
|
12
api/src/node_modules/thing/package.json
generated
vendored
Normal file
12
api/src/node_modules/thing/package.json
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "thing",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
96
api/src/node_modules/thing/thing.js
generated
vendored
Normal file
96
api/src/node_modules/thing/thing.js
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
function thing(value){
|
||||
return createThing(value)
|
||||
}
|
||||
|
||||
function createThing(value, seen = [], proxyMap = {}){
|
||||
|
||||
if (typeof value === 'object' && seen.includes(value)) {
|
||||
return proxyMap[seen.indexOf(value)]
|
||||
}
|
||||
seen.push(value)
|
||||
|
||||
const thing = toThing(value)
|
||||
proxyMap[seen.length-1] = thing
|
||||
|
||||
if (thing instanceof Object) {
|
||||
const keys = Object.keys(thing)
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i]
|
||||
if (key !== 'thingtime') {
|
||||
thing[key] = createThing(thing[key], seen, proxyMap)
|
||||
}
|
||||
}
|
||||
} else if (thing instanceof Array) {
|
||||
for (let i = 0; i < thing.length; i++) {
|
||||
thing[i] = createThing(thing[i], seen, proxyMap)
|
||||
}
|
||||
}
|
||||
|
||||
return thing
|
||||
|
||||
}
|
||||
|
||||
|
||||
function toThing(value) {
|
||||
|
||||
// if (value && value.thingtime) return value
|
||||
|
||||
// turn strings into things
|
||||
if (false && typeof value !== 'object') {
|
||||
const objValue = Object(value)
|
||||
return new Proxy(objValue, {
|
||||
get(target, prop, receiver){
|
||||
if (prop === 'uuid') {
|
||||
return uuidv4()
|
||||
} else if (prop === 'isThing') {
|
||||
return true
|
||||
} else if (prop === 'raw') {
|
||||
return value
|
||||
}
|
||||
return value[prop]
|
||||
}
|
||||
})
|
||||
} else if (['object', 'function'].includes(typeof value)){
|
||||
// value.thingtime = thingtime(value.thingtime)
|
||||
value.uuid = thingtime(value)
|
||||
return value
|
||||
// const uuid = value.uuid
|
||||
// delete value.uuid
|
||||
// return new Proxy(value, {
|
||||
// get(target, prop, receiver){
|
||||
// if (prop === 'uuid') {
|
||||
// return uuid || uuidv4()
|
||||
// } else if (prop === 'isThing') {
|
||||
// return true
|
||||
// } else if (prop === 'raw') {
|
||||
// return value
|
||||
// }
|
||||
// return Reflect.get(...arguments);
|
||||
// },
|
||||
// getPrototypeOf: function(x){
|
||||
// return String.prototype;
|
||||
// }
|
||||
// })
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
function thingtime(value) {
|
||||
|
||||
return value.uuid || uuidv4()
|
||||
|
||||
// thingtime = thingtime || {}
|
||||
|
||||
// thingtime.uuid = thingtime.uuid || uuidv4()
|
||||
|
||||
// return thingtime
|
||||
|
||||
}
|
||||
|
||||
function toRaw(value) {
|
||||
return value.raw
|
||||
}
|
||||
|
||||
export { thing as default, thing }
|
196
api/src/node_modules/thingtime/index.js
generated
vendored
Normal file
196
api/src/node_modules/thingtime/index.js
generated
vendored
Normal file
@ -0,0 +1,196 @@
|
||||
// Mongodb setup
|
||||
import { MongoClient } from 'mongodb'
|
||||
import mapObjects from 'mapObjects'
|
||||
import extractObjects from 'extractObjects'
|
||||
import extractUuids from 'extractUuids'
|
||||
import extractDependancies from 'extractDependancies'
|
||||
import extractAllDependancies from 'extractAllDependancies'
|
||||
import extractDependantPaths from 'extractDependantPaths'
|
||||
import uuidToId from 'uuidToId'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import s from 'smarts'
|
||||
const smarts = s()
|
||||
|
||||
const thingtime = {
|
||||
async init(){
|
||||
await new Promise((resolve, reject) => {
|
||||
try {
|
||||
const url = `mongodb+srv://${process.env.MONGODB_USER}:${process.env.MONGODB_PWD}@${process.env.MONGODB_CLUSTER}.nhb33.mongodb.net/${process.env.MONGODB_DB}?retryWrites=true&w=majority`
|
||||
console.log('Connecting to MongoDB with url', url)
|
||||
const client = new MongoClient(url, { useNewUrlParser: true, useUnifiedTopology: true })
|
||||
|
||||
// Connect to client
|
||||
client.connect(err => {
|
||||
if (err) {
|
||||
console.error('Connection failed', err)
|
||||
} else {
|
||||
console.log('Connected to MongoDB')
|
||||
thingtime.cache = client.db(process.env.MONGODB_DB).collection('cache')
|
||||
thingtime.things = client.db(process.env.MONGODB_DB).collection('things')
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
})
|
||||
},
|
||||
async save(thing, notify = true) {
|
||||
|
||||
const objects = extractObjects(thing)
|
||||
const objectMap = mapObjects(objects)
|
||||
const allUuids = extractUuids(objects)
|
||||
|
||||
for (let object of objects) {
|
||||
const metaObject = {
|
||||
js: object,
|
||||
uuid: object.uuid
|
||||
}
|
||||
try {
|
||||
await thingtime.things.findOneAndUpdate({
|
||||
uuid: metaObject.uuid
|
||||
},
|
||||
{
|
||||
$set: metaObject
|
||||
},
|
||||
{
|
||||
upsert: true
|
||||
})
|
||||
} catch {
|
||||
// handle rollback
|
||||
}
|
||||
|
||||
// Save dependancies
|
||||
await thingtime.saveDependancies(object)
|
||||
|
||||
// Update dependants
|
||||
await thingtime.updateDependants(object, allUuids)
|
||||
|
||||
}
|
||||
|
||||
if (notify) {
|
||||
// notify listeners
|
||||
this.notifyListeners(allUuids, objectMap)
|
||||
}
|
||||
|
||||
|
||||
console.log('Saved all')
|
||||
|
||||
},
|
||||
async updateDependants(object, ignoreDependancies = []) {
|
||||
|
||||
/**
|
||||
* This function updates all dependants of an object
|
||||
* with the value of the object at the path it is a dependant at
|
||||
*/
|
||||
|
||||
const dependants = await thingtime.things.find({
|
||||
'js.type': 'dependancy',
|
||||
'js.dependancyUuid': object.uuid
|
||||
}).toArray()
|
||||
|
||||
for (const dependant of dependants) {
|
||||
|
||||
if (!ignoreDependancies.includes(dependant.js.objectUuid)) {
|
||||
const dependantObject = await thingtime.things.findOne({
|
||||
uuid: dependant.js.objectUuid
|
||||
})
|
||||
|
||||
const dependantPaths = extractDependantPaths(dependantObject, object.uuid)
|
||||
|
||||
const toSet = {}
|
||||
|
||||
for (const dependantPath of dependantPaths) {
|
||||
toSet[dependantPath] = object
|
||||
}
|
||||
|
||||
await thingtime.things.findOneAndUpdate({
|
||||
uuid: dependantObject.uuid
|
||||
}, {
|
||||
$set: toSet
|
||||
})
|
||||
}
|
||||
|
||||
console.log()
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
async saveDependancies(object) {
|
||||
|
||||
const dependancies = extractDependancies(object)
|
||||
|
||||
if (dependancies.length) {
|
||||
|
||||
const dependanciesDb = await thingtime.things.find({
|
||||
'js.type': 'dependancy',
|
||||
'js.dependancyUuid': {
|
||||
$in: dependancies
|
||||
},
|
||||
'js.objectUuid': object.uuid
|
||||
}).toArray()
|
||||
|
||||
for (const dependancy of dependancies) {
|
||||
try {
|
||||
if (!dependanciesDb.find(o => o.js.dependancyUuid === dependancy)) {
|
||||
const dependancyToSave = {
|
||||
type: 'dependancy',
|
||||
dependancyUuid: dependancy,
|
||||
objectUuid: object.uuid,
|
||||
uuid: uuidv4()
|
||||
}
|
||||
await thingtime.save(dependancyToSave, false)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Caught error saving dependancies', err)
|
||||
// handle rollback
|
||||
}
|
||||
}
|
||||
|
||||
for (const dependancy of dependanciesDb) {
|
||||
try {
|
||||
if (!dependancies.includes(dependancy.js.dependancyUuid)) {
|
||||
await thingtime.things.deleteOne({
|
||||
uuid: dependancy.uuid
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Caught error checking whether to delete dependancy', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
async get(uuid) {
|
||||
const result = await thingtime.things.findOne({
|
||||
uuid
|
||||
})
|
||||
return result && result.js
|
||||
},
|
||||
listeners: {},
|
||||
registerListener(socket, uuid) {
|
||||
thingtime.listeners[uuid] = thingtime.listeners[uuid] || []
|
||||
|
||||
if (!thingtime.listeners[uuid].includes(socket)) {
|
||||
thingtime.listeners[uuid].push(socket)
|
||||
}
|
||||
|
||||
},
|
||||
notifyListeners(uuids, thingsMap) {
|
||||
|
||||
for (const uuid of uuids) {
|
||||
const listeners = thingtime.listeners[uuid]
|
||||
if (listeners) {
|
||||
for (const listener of listeners) {
|
||||
const thing = thingsMap[uuid]
|
||||
listener.emit('thing', {
|
||||
uuid,
|
||||
thing: smarts.serialize(thing)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default thingtime
|
12
api/src/node_modules/thingtime/package.json
generated
vendored
Normal file
12
api/src/node_modules/thingtime/package.json
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "things",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
27
api/src/node_modules/uuidToId/index.js
generated
vendored
Normal file
27
api/src/node_modules/uuidToId/index.js
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
|
||||
const uuidToId = function(thing, seen = []) {
|
||||
|
||||
if (['object', 'function'].includes(typeof thing) && thing.uuid) {
|
||||
const keys = Object.keys(thing)
|
||||
if (!thing._id) {
|
||||
thing._id = thing.uuid
|
||||
}
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i]
|
||||
if (key !== 'thingtime') {
|
||||
const value = thing[key]
|
||||
if (
|
||||
['object', 'function'].includes(typeof value)
|
||||
&& value.uuid
|
||||
&& !seen.includes(value)
|
||||
) {
|
||||
|
||||
seen.push(value)
|
||||
uuidToId(value, seen)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { uuidToId as default, uuidToId }
|
12
api/src/node_modules/uuidToId/package.json
generated
vendored
Normal file
12
api/src/node_modules/uuidToId/package.json
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "extractObjects",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
12
api/src/package.json
Normal file
12
api/src/package.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "node",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
3
api/src/test.js
Normal file
3
api/src/test.js
Normal file
@ -0,0 +1,3 @@
|
||||
import tests from 'tests'
|
||||
|
||||
tests()
|
3494
api/yarn-error.log
Normal file
3494
api/yarn-error.log
Normal file
File diff suppressed because it is too large
Load Diff
3565
api/yarn.lock
Normal file
3565
api/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -18,6 +18,7 @@
|
||||
"eslint": "8.35.0",
|
||||
"eslint-config-next": "13.2.1",
|
||||
"framer-motion": "^10.0.1",
|
||||
"hex-rgb": "^5.0.0",
|
||||
"next": "13.2.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
|
6
app/src/modules/rgb.ts
Normal file
6
app/src/modules/rgb.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import hexRgb from 'hex-rgb'
|
||||
|
||||
export default (hex: string, alpha: number) => {
|
||||
const rgb = hexRgb(hex)
|
||||
return `rgba(${rgb.red}, ${rgb.green}, ${rgb.blue}, ${typeof alpha === 'number' ? alpha : rgb.alpha})`
|
||||
}
|
@ -6,8 +6,12 @@ import { Card } from '@chakra-ui/card'
|
||||
import { Flex } from '@chakra-ui/layout'
|
||||
import { colors } from '@/chakra/theme/colors'
|
||||
const inter = Inter({ subsets: ['latin'] })
|
||||
import rgb from "@/modules/rgb"
|
||||
|
||||
export default function Home() {
|
||||
|
||||
console.log('nik rgb', rgb(colors.green, 0))
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
@ -22,7 +26,7 @@ export default function Home() {
|
||||
_hover={{
|
||||
cursor: "pointer"
|
||||
}}
|
||||
textShadow={`0px 0px 8px ${colors.green}`}
|
||||
textShadow={`0px 0px 16px ${rgb(colors.green, 1)}`}
|
||||
>
|
||||
Thing Time
|
||||
</Flex>
|
||||
|
3328
app/yarn.lock
Normal file
3328
app/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,13 @@ module.exports = {
|
||||
script: 'npm run app',
|
||||
name: "thingtime-app",
|
||||
namespace: "thingtime"
|
||||
}
|
||||
},
|
||||
{
|
||||
script: 'npm run api',
|
||||
name: 'thingtime-api',
|
||||
namespace: "thingtime",
|
||||
watch: ['node', 'node/*/node_modules', 'node/**/node_modules', 'node/node_modules'],
|
||||
ignore_watch: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -5,8 +5,8 @@
|
||||
"main": "none",
|
||||
"scripts": {
|
||||
"app": "npm run dev --prefix app",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"postinstall": "npm ci --prefix=app"
|
||||
"api": "npm run dev --prefix api",
|
||||
"postinstall": "npm ci --prefix=app ; npm ci --prefix=api"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
Loading…
Reference in New Issue
Block a user