Thing Time 📦

This commit is contained in:
Nikolaj Frey 2023-03-08 17:57:26 +11:00
parent 78a5c4d26e
commit 342de0aeb0
41 changed files with 14774 additions and 6 deletions

8
.gitignore vendored
View File

@ -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
View File

@ -0,0 +1,2 @@
PORT=3847
API_KEY="key"

20
api/.eslintrc.js Normal file
View 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
View File

@ -0,0 +1,9 @@
push
push
push
push
push
push
push
push
push

53
api/package.json Normal file
View 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

File diff suppressed because it is too large Load Diff

122
api/src/index.js Normal file
View 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
View 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 }

View 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
View 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
View 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
View 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 }

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,3 @@
import tests from 'tests'
tests()

3494
api/yarn-error.log Normal file

File diff suppressed because it is too large Load Diff

3565
api/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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
View 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})`
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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: [],
},
],
};

View File

@ -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",