working screen implementation in node

This commit is contained in:
Boaz Sender 2025-03-29 17:26:39 -07:00
parent 0de30b6f78
commit a8fc286ed2
30 changed files with 1159 additions and 614 deletions

1
.env Normal file
View file

@ -0,0 +1 @@
DATABASE_URL="file:./prisma/data.db"

1
.gitignore vendored
View file

@ -1 +1,2 @@
node_modules
prisma/data.db

8
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,8 @@
{
"recommendations": [
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss",
"dbaeumer.vscode-eslint",
"prisma.prisma"
]
}

23
build/screen.js Normal file
View file

@ -0,0 +1,23 @@
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
import LCD from "raspberrypi-liquid-crystal";
const lcd = new LCD(1, 0x27, 16, 2);
lcd.beginSync();
lcd.clearSync();
lcd.printSync('Black');
lcd.setCursorSync(0, 1);
lcd.printSync('Portal');
setInterval(async () => {
const stanzaCount = await prisma.stanza.count();
const skip = Math.floor(Math.random() * stanzaCount);
const randomStanza = await prisma.stanza.findMany({
take: 1,
skip: skip,
});
if (randomStanza) {
lcd.clearSync();
lcd.printSync(randomStanza[0].text.substring(0, 16));
lcd.setCursorSync(0, 1);
lcd.printSync(randomStanza[0].text.substring(16, 32));
}
}, 3000);

View file

@ -1,22 +0,0 @@
MIT License
Copyright (c) Remix Software Inc. 2021
Copyright (c) Shopify Inc. 2022-2023
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,181 +0,0 @@
# DIY Deploy
This project includes ansible scripts for locking down and provisioning a node.js server, and deploying something you can `npm start` to that server. That server can be bare metal, or a Virtual Private Server (VPS). These scripts make it so you never have to shell into your server by hand. You can copy or submodule these scripts into your project, which we detail in the setup steps below.
## Create or cat ssh keys
If you already have an ssh key pair, `cat ~/.ssh/id_rsa.pub` or `~/.ssh/id_ed25519.pub` depending on how long ago you did this for the first time.
If you don't have a key pair yet, then generate one:
```sh
ssh-keygen -t ed25519 -C "you@example.com"
```
Hit enter three times to save the key pair in the default location and not use a passphrase.
## Get a server
## Install Ansible
Next let's install `ansible`:
### On a mac
[Install homebrew](https://docs.brew.sh/Installation) and then:
```sh
brew update && brew install ansible
```
### On Linux
```sh
sudo apt-add-repository ppa:ansible/ansible && sudo apt update && sudo apt install ansible
```
### On Windows
Follow the [Ansible Windows Installation Instructions](https://docs.ansible.com/ansible/latest/os_guide/windows_setup.html)
## Provision a new server
If you don't have one yet, try a droplet on [digital ocean](https://digitalocean.com/). Something with 1gb of memory should be enough to start you off. Point a domain at that droplet, and replace `privacy-stack-template.com` with that domain in the following instructions. An IP address will work as well.
### Manually copy or submodule this project into your existing node app
If you want to use a submodule:
```sh
cd /path/to/myproject && git submodule add https://github.com/bocoup/deploy.git && git submodule init
```
### Copy an update inventory.example.yml
These ansible playbooks uses the variables in an inventory to know what server to deploy to, what domain to use, what version of node to install, and if you have a root ssh password, what password to use.
First copy the example inventory file:
```sh
cp deploy/inventory.example.yml inventory.yml
```
Then change the values in that file to match your project:
```yml
# Our production server.
# Copy this whole block if you'd like to add a staging server
Production:
# The IP address of your server.
# Add a second one if you'd like to deploy twice.
# You can add as many as you want.
hosts: an.ip.add.ress
vars:
# Used for your app's domain name
domain: example.com
# Used for your certbot email, note you'll be agreeing to the ToS.
email: you@example.com
# Pick your node version
nodejs_version: 20
# If you have an SSH key on your server for the root user, you don't need this.
ansible_ssh_pass: "secret"
# List of files and folders to copy to the server on deploy.
# Change this to be the files your node app needs to run.
# Example set up for a remix.run indie stack app.
deploy_files:
- src: ../prisma/migrations
dest: /home/{{ domain }}/prisma/
- src: ../prisma/schema.prisma
dest: /home/{{ domain }}/prisma/schema.prisma
- src: ../build/
dest: /home/{{ domain }}/build
- src: ../public/
dest: /home/{{ domain }}/public
- src: ../.env
dest: /home/{{ domain }}/
- src: ../.npmrc
dest: /home/{{ domain }}/
- src: ../package.json
dest: /home/{{ domain }}/
- src: ../package-lock.json
dest: /home/{{ domain }}/
- src: ../LICENSE.md
dest: /home/{{ domain }}/
- src: ../README.md
dest: /home/{{ domain }}/
```
Now you're ready to use ansible in your project.
### Lock down the server
First we'll lock down the server. If you have a ssh key already on the server, you can run:
```sh
ansible-playbook -i inventory.yml deploy/lockdown.yml
```
If you have a root user and password, you need to disable host key checking as part of your command:
```sh
export ANSIBLE_HOST_KEY_CHECKING=false && ansible-playbook -i deployment/inventory.yml deploy/lockdown.yml
```
In either case this script uses ansible to:
- Add a new user named deploy
- Add deploy user to the sudoers
- Deploy your SSH Key
- Disable Password Authentication
- Disable Root Login
- restart ssh
### Provision the server
Now let's configure the server:
```sh
ansible-playbook -i inventory.yml deploy/provision.yml
```
This script uses ansible to:
- Update apt repo and cache
- Upgrade all packages
- Check if a reboot is needed
- Reboot the server if kernel updated
- Install system packages with apt (curl, gnupg, ufw, nginx, and python3-certbot-nginx)
- Enable ufw firewall
- Enable SSH and nginx in UFW
- Create directory for the app
- Copy nginx conf to server and symlink it
- Create ssl certificate with certbot
- Copy systemd service to server
- Copy systemd friendly start script to server
- Reload and enable systemd service
- Restart nginx
- Install nvm and use it to install node and npm
## Deploy the software
Next up let's build and deploy your project!
```sh
npm run build && ansible-playbook -i inventory.yml deploy/deploy.yml
```
This script uses ansible to:
- Copy the app to the server (prisma migrations and schema, build files, public files, .env, .npmrc, package.json, package-lock.json, LICENSE.md, and README.md)
- Install npm deps
- Run migrations
- Start the app with systemd
## Make package.json commands
We also recommend that you make a deploy command to alias ansible for convenience:
```json
"scripts": {
"deploy": "npm run build && ansible-playbook -i inventory.yml deploy/deploy.yml"
},
```

View file

@ -1,26 +0,0 @@
- name: Deploy the app
hosts: all
remote_user: deploy
tasks:
- name: Copy app files to server
synchronize:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
loop: "{{ deploy_files }}"
- name: Install npm deps
shell:
cmd: "source /home/deploy/.nvm/nvm.sh && nvm exec default npm install"
chdir: /home/{{ domain }}
args:
executable: /bin/bash
- name: Run migrations
shell:
cmd: "source /home/deploy/.nvm/nvm.sh && nvm exec default npx prisma migrate deploy && nvm exec default npx prisma generate"
chdir: /home/{{ domain }}
args:
executable: /bin/bash
- name: Start the app with systemd
shell: "sudo systemctl restart {{ domain }}"

View file

@ -1,40 +0,0 @@
# Our production server.
# Copy this whole block if you'd like to add a staging server
Production:
# The IP address of your server.
# Add a second one if you'd like to deploy twice.
# You can add as many as you want.
hosts: an.ip.add.ress
vars:
# Used for your app's domain name
domain: example.com
# Used for your certbot email, note you'll be agreeing to the ToS.
email: you@example.com
# Pick your node version
nodejs_version: 20
# If you have an SSH key on your server for the root user, you don't need this
ansible_ssh_pass: "secret"
# List of files and folders to copy to the server on deploy.
# Change this to be the files your node app needs to run.
# Example set up for a remix.run indie stack app.
deploy_files:
- src: ../prisma/migrations
dest: /home/{{ domain }}/prisma/
- src: ../prisma/schema.prisma
dest: /home/{{ domain }}/prisma/schema.prisma
- src: ../build/
dest: /home/{{ domain }}/build
- src: ../public/
dest: /home/{{ domain }}/public
- src: ../.env
dest: /home/{{ domain }}/
- src: ../.npmrc
dest: /home/{{ domain }}/
- src: ../package.json
dest: /home/{{ domain }}/
- src: ../package-lock.json
dest: /home/{{ domain }}/
- src: ../LICENSE.md
dest: /home/{{ domain }}/
- src: ../README.md
dest: /home/{{ domain }}/

View file

@ -1,40 +0,0 @@
# Our production server.
# Copy this whole block if you'd like to add a staging server
Production:
# The IP address of your server.
# Add a second one if you'd like to deploy twice.
# You can add as many as you want.
hosts: an.ip.add.ress
vars:
# Used for your app's domain name
domain: example.com
# Used for your certbot email, note you'll be agreeing to the ToS.
email: you@example.com
# Pick your node version
nodejs_version: 20
# If you have an SSH key on your server for the root user, you don't need this
ansible_ssh_pass: "secret"
# List of files and folders to copy to the server on deploy.
# Change this to be the files your node app needs to run.
# Example set up for a remix.run indie stack app.
deploy_files:
- src: ../prisma/migrations
dest: /home/{{ domain }}/prisma/
- src: ../prisma/schema.prisma
dest: /home/{{ domain }}/prisma/schema.prisma
- src: ../build/
dest: /home/{{ domain }}/build
- src: ../public/
dest: /home/{{ domain }}/public
- src: ../.env
dest: /home/{{ domain }}/
- src: ../.npmrc
dest: /home/{{ domain }}/
- src: ../package.json
dest: /home/{{ domain }}/
- src: ../package-lock.json
dest: /home/{{ domain }}/
- src: ../LICENSE.md
dest: /home/{{ domain }}/
- src: ../README.md
dest: /home/{{ domain }}/

View file

@ -1,40 +0,0 @@
- name: Add deploy user and disable root user
hosts: all
vars:
remote_user: root
tasks:
- name: Add a new user named deploy
user: name=deploy
- name: Add deploy user to the sudoers
copy:
dest: "/etc/sudoers.d/deploy"
content: "deploy ALL=(ALL) NOPASSWD: ALL"
- name: Deploy your SSH Key
authorized_key: user=deploy
key="{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
state=present
- name: Disable Password Authentication
lineinfile: dest=/etc/ssh/sshd_config
regexp='^PasswordAuthentication'
line="PasswordAuthentication no"
state=present
backup=yes
notify:
- restart ssh
- name: Disable Root Login
lineinfile: dest=/etc/ssh/sshd_config
regexp='^PermitRootLogin'
line="PermitRootLogin no"
state=present
backup=yes
notify:
- restart ssh
handlers:
- name: restart ssh
service: name=ssh
state=restarted

View file

@ -1,114 +0,0 @@
- name: Update and upgrade apt packages
hosts: all
remote_user: deploy
become: yes
tasks:
- name: Update apt repo and cache
apt:
update_cache: yes
force_apt_get: yes
cache_valid_time: 3600
- name: Upgrade all packages
apt:
upgrade: dist
force_apt_get: yes
- name: Check if a reboot is needed
register: reboot_required_file
stat:
path: /var/run/reboot-required
- name: Reboot the server if kernel updated
reboot:
msg: "Reboot initiated by Ansible for kernel updates"
connect_timeout: 5
reboot_timeout: 300
pre_reboot_delay: 0
post_reboot_delay: 30
test_command: uptime
when: reboot_required_file.stat.exists
- name: Install packages
hosts: all
remote_user: deploy
become: true
tasks:
- name: Install system packages with apt
register: updatesys
apt:
update_cache: yes
name:
- curl
- gnupg
- ufw
- nginx
- python3-certbot-nginx
state: present
- name: Enable ufw firewall
community.general.ufw:
state: enabled
- community.general.ufw:
rule: allow
name: OpenSSH
- community.general.ufw:
rule: allow
name: "Nginx Full"
- name: Create directory for the app
file: path=/home/{{domain}}
state=directory
owner=deploy
group=deploy
- name: Copy nginx conf to server
template: src=./templates/nginx.conf
dest=/etc/nginx/sites-available/{{ domain }}.conf
- name: Create symlink to new nginx conf
file: src=/etc/nginx/sites-available/{{ domain }}.conf
dest=/etc/nginx/sites-enabled/{{ domain }}.conf
state=link
- name: Create ssl certificate with certbot
shell: "sudo certbot --nginx -d {{ domain }} --agree-tos --email {{ email }} --non-interactive"
notify: Restart nginx
- name: Copy systemd service to server
template: src=./templates/systemd.service
dest=/lib/systemd/system/{{ domain }}.service
- name: Copy systemd friendly start script to server
template:
src: ./templates/start.sh
dest: /home/{{ domain }}/start.sh
mode: +x
- name: Reload and enable systemd service
shell: "sudo systemctl daemon-reload && sudo systemctl enable --now {{ domain }} && sudo systemctl start {{ domain }}"
handlers:
- name: Restart nginx
service:
name: nginx
state: restarted
- name: Install node and the app
hosts: all
remote_user: deploy
tasks:
- name: Install nvm
shell: >
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
args:
creates: "{{ ansible_env.HOME }}/.nvm/nvm.sh"
- name: Install node and set version
shell: >
source ~/.nvm/nvm.sh && nvm install {{ nodejs_version }} && nvm use {{ nodejs_version }}
args:
executable: /bin/bash

View file

@ -1,22 +0,0 @@
server {
server_name www.{{ domain }};
rewrite ^(.*) http://{{ domain }}$1 permanent;
}
server {
listen 80;
listen [::]:80;
server_name {{ domain }};
access_log /var/log/nginx/{{ domain }}.log;
error_log /var/log/nginx/{{ domain }}-error.log error;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:3000;
proxy_redirect off;
client_max_body_size 10M;
}
}

View file

@ -1,3 +0,0 @@
#!/bin/bash
. /home/deploy/.nvm/nvm.sh
npm start

View file

@ -1,14 +0,0 @@
[Unit]
Description={{ domain }}
After=network.target
[Service]
Environment=NODE_ENV=production
Type=simple
User=root
WorkingDirectory=/home/{{ domain }}
ExecStart=/home/{{ domain }}/start.sh
Restart=on-failure
[Install]
WantedBy=multi-user.target

View file

@ -99,3 +99,32 @@ wget https://peppe8o.com/download/python/lcd/i2c-lcd-test-01-hello-world.py
i2cdetect -y 1
python3 i2c-lcd-test-01-hello-world.py
---
curl https://get.volta.sh | bash
volta install node
git clone git@git.featherboaz.com:boazsender/portal.git
cd portal && npm install
npx prisma generate && npx prisma db push && npx prisma db seed
sudo vim /lib/systemd/system/screen.service
```
[Unit]
Description=Screen Service
[Service]
User=root
Type=simple
Restart=on-failure
RestartSec=5
WorkingDirectory=/home/grace
ExecStart=/home/grace/.volta/bin/node /home/grace/portal/build/screen.js
[Install]
WantedBy=multi-user.target
```
sudo systemctl daemon-reload
sudo systemctl enable listener

786
docs/package-lock.json generated Normal file
View file

@ -0,0 +1,786 @@
{
"name": "portal",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "portal",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"@prisma/client": "^6.5.0",
"raspberrypi-liquid-crystal": "^1.20.0"
},
"devDependencies": {
"@types/node": "^22.13.14",
"prisma": "^6.5.0",
"tsx": "^4.19.3",
"typescript": "^5.8.2"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz",
"integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz",
"integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz",
"integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz",
"integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz",
"integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz",
"integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz",
"integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz",
"integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz",
"integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz",
"integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz",
"integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz",
"integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz",
"integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz",
"integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz",
"integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz",
"integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz",
"integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz",
"integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz",
"integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz",
"integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz",
"integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz",
"integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz",
"integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz",
"integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz",
"integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@prisma/client": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.5.0.tgz",
"integrity": "sha512-M6w1Ql/BeiGoZmhMdAZUXHu5sz5HubyVcKukbLs3l0ELcQb8hTUJxtGEChhv4SVJ0QJlwtLnwOLgIRQhpsm9dw==",
"hasInstallScript": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18.18"
},
"peerDependencies": {
"prisma": "*",
"typescript": ">=5.1.0"
},
"peerDependenciesMeta": {
"prisma": {
"optional": true
},
"typescript": {
"optional": true
}
}
},
"node_modules/@prisma/config": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.5.0.tgz",
"integrity": "sha512-sOH/2Go9Zer67DNFLZk6pYOHj+rumSb0VILgltkoxOjYnlLqUpHPAN826vnx8HigqnOCxj9LRhT6U7uLiIIWgw==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"esbuild": ">=0.12 <1",
"esbuild-register": "3.6.0"
}
},
"node_modules/@prisma/debug": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.5.0.tgz",
"integrity": "sha512-fc/nusYBlJMzDmDepdUtH9aBsJrda2JNErP9AzuHbgUEQY0/9zQYZdNlXmKoIWENtio+qarPNe/+DQtrX5kMcQ==",
"devOptional": true,
"license": "Apache-2.0"
},
"node_modules/@prisma/engines": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.5.0.tgz",
"integrity": "sha512-FVPQYHgOllJklN9DUyujXvh3hFJCY0NX86sDmBErLvoZjy2OXGiZ5FNf3J/C4/RZZmCypZBYpBKEhx7b7rEsdw==",
"devOptional": true,
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "6.5.0",
"@prisma/engines-version": "6.5.0-73.173f8d54f8d52e692c7e27e72a88314ec7aeff60",
"@prisma/fetch-engine": "6.5.0",
"@prisma/get-platform": "6.5.0"
}
},
"node_modules/@prisma/engines-version": {
"version": "6.5.0-73.173f8d54f8d52e692c7e27e72a88314ec7aeff60",
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.5.0-73.173f8d54f8d52e692c7e27e72a88314ec7aeff60.tgz",
"integrity": "sha512-iK3EmiVGFDCmXjSpdsKGNqy9hOdLnvYBrJB61far/oP03hlIxrb04OWmDjNTwtmZ3UZdA5MCvI+f+3k2jPTflQ==",
"devOptional": true,
"license": "Apache-2.0"
},
"node_modules/@prisma/fetch-engine": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.5.0.tgz",
"integrity": "sha512-3LhYA+FXP6pqY8FLHCjewyE8pGXXJ7BxZw2rhPq+CZAhvflVzq4K8Qly3OrmOkn6wGlz79nyLQdknyCG2HBTuA==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "6.5.0",
"@prisma/engines-version": "6.5.0-73.173f8d54f8d52e692c7e27e72a88314ec7aeff60",
"@prisma/get-platform": "6.5.0"
}
},
"node_modules/@prisma/get-platform": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.5.0.tgz",
"integrity": "sha512-xYcvyJwNMg2eDptBYFqFLUCfgi+wZLcj6HDMsj0Qw0irvauG4IKmkbywnqwok0B+k+W+p+jThM2DKTSmoPCkzw==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "6.5.0"
}
},
"node_modules/@types/node": {
"version": "22.13.14",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.14.tgz",
"integrity": "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.20.0"
}
},
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"license": "MIT",
"dependencies": {
"file-uri-to-path": "1.0.0"
}
},
"node_modules/debug": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/esbuild": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz",
"integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==",
"devOptional": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.25.1",
"@esbuild/android-arm": "0.25.1",
"@esbuild/android-arm64": "0.25.1",
"@esbuild/android-x64": "0.25.1",
"@esbuild/darwin-arm64": "0.25.1",
"@esbuild/darwin-x64": "0.25.1",
"@esbuild/freebsd-arm64": "0.25.1",
"@esbuild/freebsd-x64": "0.25.1",
"@esbuild/linux-arm": "0.25.1",
"@esbuild/linux-arm64": "0.25.1",
"@esbuild/linux-ia32": "0.25.1",
"@esbuild/linux-loong64": "0.25.1",
"@esbuild/linux-mips64el": "0.25.1",
"@esbuild/linux-ppc64": "0.25.1",
"@esbuild/linux-riscv64": "0.25.1",
"@esbuild/linux-s390x": "0.25.1",
"@esbuild/linux-x64": "0.25.1",
"@esbuild/netbsd-arm64": "0.25.1",
"@esbuild/netbsd-x64": "0.25.1",
"@esbuild/openbsd-arm64": "0.25.1",
"@esbuild/openbsd-x64": "0.25.1",
"@esbuild/sunos-x64": "0.25.1",
"@esbuild/win32-arm64": "0.25.1",
"@esbuild/win32-ia32": "0.25.1",
"@esbuild/win32-x64": "0.25.1"
}
},
"node_modules/esbuild-register": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz",
"integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"debug": "^4.3.4"
},
"peerDependencies": {
"esbuild": ">=0.12 <1"
}
},
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"license": "MIT"
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/get-tsconfig": {
"version": "4.10.0",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz",
"integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==",
"dev": true,
"license": "MIT",
"dependencies": {
"resolve-pkg-maps": "^1.0.0"
},
"funding": {
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
"node_modules/i2c-bus": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/i2c-bus/-/i2c-bus-5.2.3.tgz",
"integrity": "sha512-kzFgU0OSIZlaeUUa+VK7L+kkxnj4feimCVQDOPrzj0cTaB+hNweTsO8/j7QvW2gRgWQ7ds5IpfFjpBrX+fMNng==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"bindings": "^1.5.0",
"nan": "^2.17.0"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"devOptional": true,
"license": "MIT"
},
"node_modules/nan": {
"version": "2.22.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz",
"integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==",
"license": "MIT"
},
"node_modules/prisma": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-6.5.0.tgz",
"integrity": "sha512-yUGXmWqv5F4PByMSNbYFxke/WbnyTLjnJ5bKr8fLkcnY7U5rU9rUTh/+Fja+gOrRxEgtCbCtca94IeITj4j/pg==",
"devOptional": true,
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/config": "6.5.0",
"@prisma/engines": "6.5.0"
},
"bin": {
"prisma": "build/index.js"
},
"engines": {
"node": ">=18.18"
},
"optionalDependencies": {
"fsevents": "2.3.3"
},
"peerDependencies": {
"typescript": ">=5.1.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/raspberrypi-liquid-crystal": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/raspberrypi-liquid-crystal/-/raspberrypi-liquid-crystal-1.20.0.tgz",
"integrity": "sha512-hAAnoSMisosqBs9yNtRYzQw+KTCqKUY8hqlayCy+dfCIlH+DNfQFcK3fQ2D1Uj4s8P/AqZqIq3yuegB+Bl8qVQ==",
"license": "ISC",
"dependencies": {
"i2c-bus": "^5.1.0",
"sleep": "^6.3.0"
}
},
"node_modules/resolve-pkg-maps": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
"node_modules/sleep": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/sleep/-/sleep-6.3.0.tgz",
"integrity": "sha512-+WgYl951qdUlb1iS97UvQ01pkauoBK9ML9I/CMPg41v0Ze4EyMlTgFTDDo32iYj98IYqxIjDMRd+L71lawFfpQ==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"nan": "^2.14.1"
},
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/tsx": {
"version": "4.19.3",
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.3.tgz",
"integrity": "sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "~0.25.0",
"get-tsconfig": "^4.7.5"
},
"bin": {
"tsx": "dist/cli.mjs"
},
"engines": {
"node": ">=18.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
}
},
"node_modules/typescript": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
"integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
"devOptional": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
"dev": true,
"license": "MIT"
}
}
}

30
docs/package.json Normal file
View file

@ -0,0 +1,30 @@
{
"name": "portal",
"type": "module",
"version": "1.0.0",
"description": "https://blackportaldetroit.com",
"main": "index.js",
"scripts": {
"build": "tsc screen.ts",
"deploy": "npm run build && rsync -avx rsync -a --exclude-from='exclude.txt' ./ grace@192.168.0.195:portal"
},
"repository": {
"type": "git",
"url": "git@git.featherboaz.com:boazsender/portal.git"
},
"author": "",
"license": "ISC",
"dependencies": {
"@prisma/client": "^6.5.0",
"raspberrypi-liquid-crystal": "^1.20.0"
},
"devDependencies": {
"@types/node": "^22.13.14",
"prisma": "^6.5.0",
"tsx": "^4.19.3",
"typescript": "^5.8.2"
},
"prisma": {
"seed": "tsx prisma/seed.ts"
}
}

View file

Before

Width:  |  Height:  |  Size: 232 KiB

After

Width:  |  Height:  |  Size: 232 KiB

2
exclude.txt Normal file
View file

@ -0,0 +1,2 @@
node_modules
.git

84
package-lock.json generated
View file

@ -1,84 +0,0 @@
{
"name": "portal",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "portal",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"adafruit-i2c-lcd": "^2.0.0",
"raspberrypi-liquid-crystal": "^1.20.0"
}
},
"node_modules/adafruit-i2c-lcd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/adafruit-i2c-lcd/-/adafruit-i2c-lcd-2.0.0.tgz",
"integrity": "sha512-L23HBVbNyr1yGXwO0RsT6modQgqOe98EDA4OR8oo74Rhwy5xaqPjSSBFvvBc7IX7hgjjBS12udSzguCehSzrxw==",
"license": "BSD-2-Clause",
"dependencies": {
"i2c-bus": "^5.1.0"
}
},
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"license": "MIT",
"dependencies": {
"file-uri-to-path": "1.0.0"
}
},
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"license": "MIT"
},
"node_modules/i2c-bus": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/i2c-bus/-/i2c-bus-5.2.3.tgz",
"integrity": "sha512-kzFgU0OSIZlaeUUa+VK7L+kkxnj4feimCVQDOPrzj0cTaB+hNweTsO8/j7QvW2gRgWQ7ds5IpfFjpBrX+fMNng==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"bindings": "^1.5.0",
"nan": "^2.17.0"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/nan": {
"version": "2.22.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz",
"integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==",
"license": "MIT"
},
"node_modules/raspberrypi-liquid-crystal": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/raspberrypi-liquid-crystal/-/raspberrypi-liquid-crystal-1.20.0.tgz",
"integrity": "sha512-hAAnoSMisosqBs9yNtRYzQw+KTCqKUY8hqlayCy+dfCIlH+DNfQFcK3fQ2D1Uj4s8P/AqZqIq3yuegB+Bl8qVQ==",
"license": "ISC",
"dependencies": {
"i2c-bus": "^5.1.0",
"sleep": "^6.3.0"
}
},
"node_modules/sleep": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/sleep/-/sleep-6.3.0.tgz",
"integrity": "sha512-+WgYl951qdUlb1iS97UvQ01pkauoBK9ML9I/CMPg41v0Ze4EyMlTgFTDDo32iYj98IYqxIjDMRd+L71lawFfpQ==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"nan": "^2.14.1"
},
"engines": {
"node": ">=0.8.0"
}
}
}
}

View file

@ -1,18 +0,0 @@
{
"name": "portal",
"version": "1.0.0",
"description": "https://blackportaldetroit.com",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git@git.featherboaz.com:boazsender/portal.git"
},
"author": "",
"license": "ISC",
"dependencies": {
"raspberrypi-liquid-crystal": "^1.20.0"
}
}

34
prisma/schema.prisma Normal file
View file

@ -0,0 +1,34 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model Stanza {
id String @id @default(cuid())
text String
songId String
song Song @relation(fields: [songId], references: [id])
}
model Song {
id String @id @default(cuid())
name String @unique
artistId String
artist Artist @relation(fields: [artistId], references: [id])
stanzas Stanza[]
}
model Artist {
id String @id @default(cuid())
name String @unique
songs Song[]
}

99
prisma/seed.ts Normal file
View file

@ -0,0 +1,99 @@
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
const slumVillage = await prisma.artist.create({
data: {name: "Slum Village"}
});
const lookOfLove = await prisma.song.create({ data: {
name: "The Look Of Love", artistId: slumVillage.id}
});
[`What is the look of...?`,
`It got something to do with, um...`,
`Being a man and handlin' your biz`,
`What love got to do with it?`,
`Ask S.V., it's all bullshit`,
`You know what love is`,
`Say it with me one time you know what love is`,
`S.V. on some love shit you know what love is`,
`S.V. on some love shit you know what love is`,
`And to the bitches that love dick, and masturbatn'`,
`No need for that, I get down for rappin', say word`,
`("Word, Rick? Word!")`,
`(Take it back...)`,
`("Word, Rick? Word!")`,
`Take - take - take it back (Take - take...)`,
`For real...`,
`What is the look of...?`,
`It got something to do with, um...`,
`Being a man and handlin' your biz`,
`What love got to do with it?`,
`Ask S.V., it's all bullshit`,
`You know what love is`,
`Say it with me one time you know what love is`,
`S.V. on some love shit you know what love is`,
`S.V. on some love shit you know what love is`,
`The Motobot has been slept on for a long time`,
`It's time for me to put my mack down`,
`But in the meantime... ("Have you... have you...")`,
`Let me tell you 'bout a bitch named ("s-s-so...")`,
`The woman looked so good she make you masturbate`,
`Come into my life, let me show you how I operate`,
`My name is Rasul, but you can call me a gigolo`,
`My teacher named me, yup yup, you know how we roll`,
`I had a lot of shit to do, back in the astral`,
`Baatin nigga, come back to Earth`,
`What can I do for you back in this bitch?`,
`Coming through with my new suit`,
`Nigga lookin' fly, 'cause you know we got a lot of loot`,
`When I'm on the mic, a nigga throw down his blunt`,
`Just to hear what we say up in this bitch`,
`You know what love is`,
`Say it with me now you know what love is`,
`What is the look of...?`,
`It got something to do with, um...`,
`Being a man and handlin' your biz`,
`What love got to do with it?`,
`Ask S.V., it's all bullshit`,
`You know what love is`,
`T3 is on the love tip (One time) you know what love is`,
`Yo, The S is on some love tip you know what love is`,
`T3 is on some love tip you know what love is`,
`She said "I wanna get down with The S"`,
`I said, "certainly, let me teach you splendor-ness"`,
`Bustin' a fur, girl, you were scandalous`,
`Your fragrance got me losing consciousness`,
`Your stance got me unbucklin' my fuckin' pants`,
`Spending clouds of finance on a two dollar romance`,
`Take the dick to the tip, get a lick`,
`Tell a bitch, The S is the shit, so eat a dick`,
`You need to, you need to - just give me your clit`,
`As I get nasty like an old porno flick`,
`Oral sex, got me caught up in the Kaula bliss`,
`Strategic, as I drop this dick between her tits yes...`,
`You know what love is`,
`Say it with me one now...`,
`You know what love is`,
`What is the look of...?`,
`It got something to do with, um...`,
`Being a man and handlin' your biz`,
`What love got to do with it?`,
`Ask S.V., it's all bullshit`,
`You know what love is`,
`Say it with me? You know what love is (Uh-huh, uh-huh)`,
`You know what love is (You know what love is...)`,
`You know what love is`,
`You know what love is`,
`Ahh that's makin' me cry, man...!`,
`Can we cry, man? (Man, that shit is...)`,
`Can we cry, man??`,
`That shit is sentimental, man (Yeah, yeah)`,
`How you feelin'?`,].forEach(async (text) => {
await prisma.stanza.create({
data: {
text,
songId: lookOfLove.id
}
})
})

View file

@ -1,8 +0,0 @@
const LCD = require('raspberrypi-liquid-crystal');
const lcd = new LCD( 1, 0x27, 16, 2 );
lcd.beginSync();
lcd.clearSync();
lcd.printSync( 'Black' );
lcd.setCursorSync(0, 1);
lcd.printSync( 'Portal' );

31
screen.ts Normal file
View file

@ -0,0 +1,31 @@
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
import LCD from "raspberrypi-liquid-crystal"
const lcd = new LCD( 1, 0x27, 16, 2 );
lcd.beginSync();
lcd.clearSync();
lcd.printSync( 'Black' );
lcd.setCursorSync(0, 1);
lcd.printSync( 'Portal' );
setInterval(async ()=>{
const stanzaCount = await prisma.stanza.count();
const skip = Math.floor(Math.random() * stanzaCount) - 1;
const randomStanza = await prisma.stanza.findMany({
take: 2,
skip: skip,
});
if(randomStanza){
lcd.clearSync();
if(randomStanza[0].text.length < 16){
lcd.printSync( randomStanza[0].text );
lcd.setCursorSync(0, 1);
lcd.printSync( randomStanza[1].text );
}
}
}, 3000)

113
tsconfig.json Normal file
View file

@ -0,0 +1,113 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "libReplacement": true, /* Enable lib replacement. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "NodeNext", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "noUncheckedSideEffectImports": true, /* Check side effect imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "./build", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
// "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}