WIP
This commit is contained in:
35
app.webmanifest
Normal file
35
app.webmanifest
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "Dandelion",
|
||||||
|
"short_name": "ddln",
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"display_override": ["window-controls-overlay","standalone"],
|
||||||
|
"id": "b1dbe643-36fc-4419-9448-80f32a1baa1a",
|
||||||
|
"background_color": "#000000",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "icons/dandelion_512x512.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes":"512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"screenshots" : [
|
||||||
|
{
|
||||||
|
"src": "images/screenshot1.jpg",
|
||||||
|
"sizes": "1280x720",
|
||||||
|
"type": "image/png",
|
||||||
|
"form_factor": "wide",
|
||||||
|
"label": "Dandelion desktop"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "images/screenshot2.jpg",
|
||||||
|
"sizes": "720x1280",
|
||||||
|
"type": "image/png",
|
||||||
|
"form_factor": "narrow",
|
||||||
|
"label": "Dandelion mobile"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
71
db.js
71
db.js
@@ -3,12 +3,11 @@
|
|||||||
// name: string;
|
// name: string;
|
||||||
// email: string;
|
// email: string;
|
||||||
// }
|
// }
|
||||||
const dbName = "ddln";
|
const postStoreName = "posts";
|
||||||
const storeNameBase = "posts";
|
|
||||||
let keyBase = "dandelion_posts_v1_";
|
let keyBase = "dandelion_posts_v1_";
|
||||||
let key = "";
|
let key = "";
|
||||||
export function openDatabase(userID) {
|
export function openDatabase(userID) {
|
||||||
const storeName = `${storeNameBase}_${userID}`;
|
const dbName = `user_${userID}`;
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const request = indexedDB.open(dbName, 1);
|
const request = indexedDB.open(dbName, 1);
|
||||||
request.onerror = (event) => {
|
request.onerror = (event) => {
|
||||||
@@ -18,8 +17,8 @@ export function openDatabase(userID) {
|
|||||||
};
|
};
|
||||||
request.onupgradeneeded = (event) => {
|
request.onupgradeneeded = (event) => {
|
||||||
const db = event.target.result;
|
const db = event.target.result;
|
||||||
if (!db.objectStoreNames.contains(storeName)) {
|
if (!db.objectStoreNames.contains(postStoreName)) {
|
||||||
let store = db.createObjectStore(storeName, { keyPath: "id", autoIncrement: true });
|
let store = db.createObjectStore(postStoreName, { keyPath: "id", autoIncrement: true });
|
||||||
store.createIndex("datetimeIndex", "post_timestamp", { unique: false });
|
store.createIndex("datetimeIndex", "post_timestamp", { unique: false });
|
||||||
store.createIndex("postIDIndex", "data.post_id", { unique: true });
|
store.createIndex("postIDIndex", "data.post_id", { unique: true });
|
||||||
}
|
}
|
||||||
@@ -32,10 +31,9 @@ export function openDatabase(userID) {
|
|||||||
}
|
}
|
||||||
export async function addData(userID, data) {
|
export async function addData(userID, data) {
|
||||||
try {
|
try {
|
||||||
const storeName = `${storeNameBase}_${userID}`;
|
|
||||||
const db = await openDatabase(userID);
|
const db = await openDatabase(userID);
|
||||||
const transaction = db.transaction(storeName, "readwrite");
|
const transaction = db.transaction(postStoreName, "readwrite");
|
||||||
const store = transaction.objectStore(storeName);
|
const store = transaction.objectStore(postStoreName);
|
||||||
const addRequest = store.add({ post_timestamp: data.post_timestamp, data: data });
|
const addRequest = store.add({ post_timestamp: data.post_timestamp, data: data });
|
||||||
addRequest.onsuccess = (e) => {
|
addRequest.onsuccess = (e) => {
|
||||||
// console.log('Data has been added:', (e.target as IDBRequest).result);
|
// console.log('Data has been added:', (e.target as IDBRequest).result);
|
||||||
@@ -50,12 +48,53 @@ export async function addData(userID, data) {
|
|||||||
console.error('Error in opening database:', error);
|
console.error('Error in opening database:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export async function deleteData(userID, postID) {
|
||||||
|
try {
|
||||||
|
const db = await openDatabase(userID);
|
||||||
|
const transaction = db.transaction(postStoreName, "readwrite");
|
||||||
|
const store = transaction.objectStore(postStoreName);
|
||||||
|
const index = store.index("postIDIndex");
|
||||||
|
const getRequest = index.getKey(postID);
|
||||||
|
getRequest.onerror = e => console.log(e.target.error);
|
||||||
|
getRequest.onsuccess = e => {
|
||||||
|
const key = e.target.result;
|
||||||
|
if (key === undefined) {
|
||||||
|
console.error("Post not found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const deleteRequest = store.delete(key);
|
||||||
|
deleteRequest.onerror = e => { console.error(e.target.error); return false; };
|
||||||
|
deleteRequest.onsuccess = () => true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error('Error in opening database:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export async function clearData(userID) {
|
||||||
|
try {
|
||||||
|
const db = await openDatabase(userID);
|
||||||
|
const transaction = db.transaction(postStoreName, "readwrite");
|
||||||
|
const store = transaction.objectStore(postStoreName);
|
||||||
|
const clearRequest = store.clear();
|
||||||
|
clearRequest.onsuccess = (e) => {
|
||||||
|
// console.log('Data has been added:', (e.target as IDBRequest).result);
|
||||||
|
};
|
||||||
|
clearRequest.onerror = (event) => {
|
||||||
|
// Use a type assertion to access the specific properties of IDBRequest error event
|
||||||
|
const errorEvent = event;
|
||||||
|
console.error('Error in clearing data:', errorEvent.target.error?.message);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error('Error in opening database:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
export async function addDataArray(userID, array) {
|
export async function addDataArray(userID, array) {
|
||||||
try {
|
try {
|
||||||
const storeName = `${storeNameBase}_${userID}`;
|
|
||||||
const db = await openDatabase(userID);
|
const db = await openDatabase(userID);
|
||||||
const transaction = db.transaction(storeName, "readwrite");
|
const transaction = db.transaction(postStoreName, "readwrite");
|
||||||
const store = transaction.objectStore(storeName);
|
const store = transaction.objectStore(postStoreName);
|
||||||
let count = 0;
|
let count = 0;
|
||||||
array.reverse();
|
array.reverse();
|
||||||
for (let data of array) {
|
for (let data of array) {
|
||||||
@@ -79,10 +118,9 @@ export async function addDataArray(userID, array) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function getData(userID, lowerID, upperID) {
|
export async function getData(userID, lowerID, upperID) {
|
||||||
const storeName = `${storeNameBase}_${userID}`;
|
|
||||||
const db = await openDatabase(userID);
|
const db = await openDatabase(userID);
|
||||||
const transaction = db.transaction(storeName, "readonly");
|
const transaction = db.transaction(postStoreName, "readonly");
|
||||||
const store = transaction.objectStore(storeName);
|
const store = transaction.objectStore(postStoreName);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const keyRangeValue = IDBKeyRange.bound(lowerID, upperID);
|
const keyRangeValue = IDBKeyRange.bound(lowerID, upperID);
|
||||||
const records = [];
|
const records = [];
|
||||||
@@ -108,10 +146,9 @@ export async function getData(userID, lowerID, upperID) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
export async function getAllData(userID) {
|
export async function getAllData(userID) {
|
||||||
const storeName = `${storeNameBase}_${userID}`;
|
|
||||||
const db = await openDatabase(userID);
|
const db = await openDatabase(userID);
|
||||||
const transaction = db.transaction(storeName, "readonly");
|
const transaction = db.transaction(postStoreName, "readonly");
|
||||||
const store = transaction.objectStore(storeName);
|
const store = transaction.objectStore(postStoreName);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const getRequest = store.getAll();
|
const getRequest = store.getAll();
|
||||||
getRequest.onsuccess = () => {
|
getRequest.onsuccess = () => {
|
||||||
|
|||||||
8
go.mod
8
go.mod
@@ -21,6 +21,14 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/pion/stun v0.6.1
|
||||||
github.com/quic-go/qpack v0.4.0 // indirect
|
github.com/quic-go/qpack v0.4.0 // indirect
|
||||||
golang.org/x/text v0.13.0 // indirect
|
golang.org/x/text v0.13.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/andybalholm/brotli v1.1.0
|
||||||
|
github.com/pion/dtls/v2 v2.2.7 // indirect
|
||||||
|
github.com/pion/logging v0.2.2 // indirect
|
||||||
|
github.com/pion/transport/v2 v2.2.1 // indirect
|
||||||
|
)
|
||||||
|
|||||||
52
go.sum
52
go.sum
@@ -1,3 +1,5 @@
|
|||||||
|
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||||
|
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
@@ -12,29 +14,79 @@ github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZH
|
|||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
|
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
|
||||||
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
|
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
|
||||||
|
github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
|
||||||
|
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
|
||||||
|
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||||
|
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||||
|
github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4=
|
||||||
|
github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8=
|
||||||
|
github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c=
|
||||||
|
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||||
github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM=
|
github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM=
|
||||||
github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
|
github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
|
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
|
||||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
|
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
|
||||||
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
|
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
|
||||||
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
BIN
icons/dandelion_512x512.png
Normal file
BIN
icons/dandelion_512x512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
1
images/lottie_test.json
Normal file
1
images/lottie_test.json
Normal file
File diff suppressed because one or more lines are too long
BIN
images/screenshot1.jpg
Normal file
BIN
images/screenshot1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 137 KiB |
BIN
images/screenshot1.png
Normal file
BIN
images/screenshot1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 206 KiB |
BIN
images/screenshot2.jpg
Normal file
BIN
images/screenshot2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 138 KiB |
BIN
images/screenshot2.png
Normal file
BIN
images/screenshot2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 206 KiB |
98
index.html
98
index.html
@@ -7,8 +7,13 @@
|
|||||||
|
|
||||||
<title>Dandelion</title>
|
<title>Dandelion</title>
|
||||||
<script type="module" src="main.js"></script>
|
<script type="module" src="main.js"></script>
|
||||||
<script src="marked.min.js"></script>
|
<script src="lib/marked.min.js"></script>
|
||||||
|
<script src="lib/qrcode.min.js"></script>
|
||||||
|
<!-- <script src="lib/lottie.min.js"></script> -->
|
||||||
|
<!-- <script src="https://unpkg.com/@dotlottie/player-component@latest/dist/dotlottie-player.mjs" type="module"></script> -->
|
||||||
|
|
||||||
|
<!-- <script src="/lib/webtorrent/webtorrent_1_8_0.min.js"></script> -->
|
||||||
|
<link rel="manifest" href="/app.webmanifest">
|
||||||
<style>
|
<style>
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Virgil';
|
font-family: 'Virgil';
|
||||||
@@ -40,7 +45,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
border-color: rgb(128, 128, 128);
|
border-color: rgb(60, 60, 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
#textarea_post {
|
#textarea_post {
|
||||||
@@ -52,15 +57,17 @@
|
|||||||
/* Make the textarea take up 100% of the body's width */
|
/* Make the textarea take up 100% of the body's width */
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
/* Include padding and border in the element's total width and height */
|
/* Include padding and border in the element's total width and height */
|
||||||
padding: 10px;
|
padding-left: 30px;
|
||||||
/* Optional padding for the inside of the textarea */
|
padding-right: 30px;
|
||||||
border: 1px solid rgb(132, 136, 138);
|
border: 1px solid rgb(132, 136, 138);
|
||||||
/* Optional border for the textarea */
|
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
/* Allows the textarea to be resized vertically only */
|
/* Allows the textarea to be resized vertically only */
|
||||||
|
border-radius: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
.flex-container {
|
.flex-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -72,14 +79,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
max-width: 800px;
|
max-width: 600px;
|
||||||
/* Your preferred max width for the content */
|
/* Your preferred max width for the content */
|
||||||
flex: 1;
|
flex: 1;
|
||||||
/* Shorthand for flex-grow, flex-shrink and flex-basis */
|
/* Shorthand for flex-grow, flex-shrink and flex-basis */
|
||||||
min-width: 300px;
|
min-width: 300px;
|
||||||
/* Minimum width the content can shrink to */
|
/* Minimum width the content can shrink to */
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
box-shadow: 0 0 10px rgb(60, 60, 60);
|
box-shadow: 0 0 5px rgb(60, 60, 60);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
/* Hide horizontal overflow inside the flex container */
|
/* Hide horizontal overflow inside the flex container */
|
||||||
@@ -94,25 +101,64 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.postImage {
|
.postImage {
|
||||||
width:100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#log {
|
#log {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
text-wrap: nowrap;
|
text-wrap: nowrap;
|
||||||
|
font-size: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
height: 150px;
|
||||||
|
width: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#buttons {
|
||||||
|
margin-left: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#button_post {
|
||||||
|
margin-right:40px;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: rgb(29, 155, 240);
|
color: rgb(29, 155, 240);
|
||||||
}
|
}
|
||||||
|
|
||||||
#log {
|
.logo {
|
||||||
margin-bottom: 20px;
|
width: 32px;
|
||||||
height:150px;
|
height: 32px;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
}
|
||||||
|
|
||||||
|
#torrent-content {
|
||||||
|
border:solid 1px;
|
||||||
|
width:800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.img-button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: rgb(0, 0, 0);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding-left:10px;
|
||||||
|
padding-right:10px;
|
||||||
|
padding-top:5px;
|
||||||
|
padding-bottom:5px;
|
||||||
|
margin-left:5px;
|
||||||
|
color:rgb(255,255,255);
|
||||||
|
/* border:solid 1px white; */
|
||||||
|
border: 1px solid rgb(132, 136, 138);
|
||||||
|
color: rgb(202, 208, 211);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@@ -124,15 +170,29 @@
|
|||||||
|
|
||||||
<div class="flex-container">
|
<div class="flex-container">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
<div class="img-button" id="ddln-logo-button"><img class="logo" src="favicon.ico"></div>
|
||||||
|
|
||||||
<div id="status"></div>
|
<div id="status"></div>
|
||||||
<div id="log"></div>
|
|
||||||
<input type="button" value="font1" id="button_font1" />
|
<div id="info" style="display:none">
|
||||||
<input type="button" value="font2" id="button_font2" />
|
<div id="log" ></div>
|
||||||
|
<div id="qrcode"></div>
|
||||||
<textarea cols="60" rows="8" id="textarea_post"></textarea>
|
<!-- <dotlottie-player src="https://lottie.host/272b60dd-462d-42a3-8ed6-fec4143633d6/X4FxBascRI.json" background="transparent" speed="1" style="width: 300px; height: 300px" direction="1" playMode="normal" loop controls autoplay></dotlottie-player> -->
|
||||||
<div class="button">
|
|
||||||
<input type="button" value="post" id="button_post" />
|
|
||||||
</div>
|
</div>
|
||||||
|
<!-- <div id="peer_display"><canvas></canvas></div> -->
|
||||||
|
<div id="buttons">
|
||||||
|
<button id="button_font1" >font1</button>
|
||||||
|
<button id="button_font2" >font2 </button>
|
||||||
|
<button id="import_tweets" >import</button>
|
||||||
|
<button id="clear_posts" >clear </button>
|
||||||
|
<button id="update_app" >check for updates</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<textarea cols="60" rows="6" id="textarea_post"></textarea>
|
||||||
|
<div class="button">
|
||||||
|
<button id="button_post" >post</button>
|
||||||
|
</div>
|
||||||
|
<!-- <div id="torrent-content"></div> -->
|
||||||
<div id="content"></div>
|
<div id="content"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
1
lib/lottie.min.js
vendored
Normal file
1
lib/lottie.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
0
marked.min.js → lib/marked.min.js
vendored
0
marked.min.js → lib/marked.min.js
vendored
1
lib/qrcode.min.js
vendored
Normal file
1
lib/qrcode.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
16
lib/webtorrent/webtorrent_1_8_0.min.js
vendored
Normal file
16
lib/webtorrent/webtorrent_1_8_0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
163
main.go
163
main.go
@@ -34,7 +34,8 @@
|
|||||||
// This will allow distributed content delivery but put a memory and bendwidth strain on the
|
// This will allow distributed content delivery but put a memory and bendwidth strain on the
|
||||||
// bootstrap sever. Look into Web Transport for the raspberry pi overhead. Could buy a few more RPIs
|
// bootstrap sever. Look into Web Transport for the raspberry pi overhead. Could buy a few more RPIs
|
||||||
// and make a little cluster
|
// and make a little cluster
|
||||||
// Domain name so we can get a certificate and serve HTTPS / HTTP3
|
|
||||||
|
// ✅ Domain name so we can get a certificate and serve HTTPS / HTTP3
|
||||||
|
|
||||||
// Think about compiling Typescript on initial access and caching the JS in a service worker
|
// Think about compiling Typescript on initial access and caching the JS in a service worker
|
||||||
// so you don't need a build system to change things.
|
// so you don't need a build system to change things.
|
||||||
@@ -43,23 +44,59 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/andybalholm/brotli"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/quic-go/quic-go/http3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var upgrader = websocket.Upgrader{
|
var upgrader = websocket.Upgrader{
|
||||||
CheckOrigin: func(r *http.Request) bool {
|
CheckOrigin: func(r *http.Request) bool {
|
||||||
return true // Note: In production, you'd want to check the origin.
|
origin := r.Header.Get("Origin")
|
||||||
|
return origin == "https://ddlion.net"
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleWebSocket handles WebSocket requests from the peer.
|
func websocketCloseHandler(code int, text string) error {
|
||||||
|
log.Print("Client closed websocket.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MessageHandler func([]byte, *websocket.Conn) error
|
||||||
|
|
||||||
|
var messageHandlers = make(map[string]MessageHandler)
|
||||||
|
|
||||||
|
func registerHandler(messageType string, handler MessageHandler) {
|
||||||
|
messageHandlers[messageType] = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func dispatchMessage(message []byte, conn *websocket.Conn) error {
|
||||||
|
var msg Message
|
||||||
|
if err := json.Unmarshal(message, &msg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
handler, ok := messageHandlers[msg.Type]
|
||||||
|
if !ok {
|
||||||
|
log.Printf("No handler registered for message type: %s", msg.Type)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler(message, conn)
|
||||||
|
}
|
||||||
|
|
||||||
func handleWebSocket(w http.ResponseWriter, r *http.Request) {
|
func handleWebSocket(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Println("Websocket connection!", r.RemoteAddr)
|
log.Println("Websocket connection!", r.RemoteAddr)
|
||||||
|
|
||||||
@@ -70,47 +107,126 @@ func handleWebSocket(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
|
conn.SetCloseHandler(websocketCloseHandler)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
mt, message, err := conn.ReadMessage()
|
_, message, err := conn.ReadMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Read error:", err)
|
log.Println("ReadMessage error:", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
log.Printf("recv: %s", message)
|
log.Printf("recv: %s", message)
|
||||||
|
|
||||||
err = conn.WriteMessage(mt, message)
|
if err := dispatchMessage(message, conn); err != nil {
|
||||||
if err != nil {
|
log.Printf("Error dispatching message: %v", err)
|
||||||
log.Println("Write error:", err)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoggingHandler logs requests and delegates them to the underlying handler.
|
// Example handlers
|
||||||
type LoggingHandler struct {
|
func handlePing(message []byte, conn *websocket.Conn) error {
|
||||||
handler http.Handler
|
var pingMsg struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
PeerID string `json:"peer_id"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(message, &pingMsg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("Received ping from peer: %s", pingMsg.PeerID)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lh *LoggingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
type PeerSet map[string]struct{}
|
||||||
log.Printf("Serving file: %s", r.URL.Path)
|
|
||||||
lh.handler.ServeHTTP(w, r)
|
var userPeers = make(map[string]PeerSet)
|
||||||
|
var peerConnections = make(map[string]*websocket.Conn)
|
||||||
|
|
||||||
|
func handleHello(message []byte, conn *websocket.Conn) error {
|
||||||
|
|
||||||
|
var m struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
PeerID string `json:"peer_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(message, &m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if userPeers[m.UserID] == nil {
|
||||||
|
userPeers[m.UserID] = make(PeerSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
userPeers[m.UserID][m.PeerID] = struct{}{}
|
||||||
|
peerConnections[m.PeerID] = conn
|
||||||
|
|
||||||
|
jsonData, _ := json.MarshalIndent(userPeers, "", " ")
|
||||||
|
fmt.Println(string(jsonData), peerConnections)
|
||||||
|
|
||||||
|
log.Printf("Received connect from peer: %s, user:%s", m.PeerID, m.UserID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoggingHandler logs requests and delegates them to the underlying handler.
|
||||||
|
// type LoggingHandler struct {
|
||||||
|
// handler http.Handler
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (lh *LoggingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// log.Printf("Serving file: %s", r.URL.Path)
|
||||||
|
// lh.handler.ServeHTTP(w, r)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// BrotliResponseWriter wraps http.ResponseWriter to support Brotli compression
|
||||||
|
type brotliResponseWriter struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
Writer io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *brotliResponseWriter) Write(b []byte) (int, error) {
|
||||||
|
return w.Writer.Write(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// noDirListing wraps an http.FileServer handler to prevent directory listings
|
// noDirListing wraps an http.FileServer handler to prevent directory listings
|
||||||
func noDirListing(h http.Handler, root string) http.HandlerFunc {
|
func noDirListing(h http.Handler, root string) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// Serve index.html when root is requested
|
// Serve index.html when root is requested
|
||||||
if r.URL.Path == "/" {
|
if r.URL.Path == "/" {
|
||||||
http.ServeFile(w, r, filepath.Join(root, "index.html"))
|
http.ServeFile(w, r, filepath.Join(root, "index.html"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the path is a directory
|
|
||||||
path := filepath.Join(root, r.URL.Path)
|
path := filepath.Join(root, r.URL.Path)
|
||||||
if info, err := os.Stat(path); err == nil && info.IsDir() {
|
info, err := os.Stat(path)
|
||||||
http.NotFound(w, r) // Return 404 for directories other than root
|
if err != nil || info.IsDir() {
|
||||||
|
log.Printf("404 File not found/dir serving: %s to ip %s, useragent %s", r.URL.Path, r.RemoteAddr, r.UserAgent())
|
||||||
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("Serving: %s to ip %s, useragent %s", r.URL.Path, r.RemoteAddr, r.UserAgent())
|
||||||
|
|
||||||
|
// Check if client supports Brotli encoding
|
||||||
|
if strings.Contains(r.Header.Get("Accept-Encoding"), "br") {
|
||||||
|
w.Header().Set("Content-Encoding", "br")
|
||||||
|
w.Header().Del("Content-Length") // Cannot know content length with compressed data
|
||||||
|
|
||||||
|
// Wrap the ResponseWriter with Brotli writer
|
||||||
|
brWriter := brotli.NewWriter(w)
|
||||||
|
defer brWriter.Close()
|
||||||
|
|
||||||
|
// Create a ResponseWriter that writes to brWriter
|
||||||
|
bw := &brotliResponseWriter{
|
||||||
|
ResponseWriter: w,
|
||||||
|
Writer: brWriter,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve the file using http.ServeFile
|
||||||
|
http.ServeFile(bw, r, path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
h.ServeHTTP(w, r)
|
h.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,8 +238,9 @@ func main() {
|
|||||||
addr := ":" + strconv.Itoa(port)
|
addr := ":" + strconv.Itoa(port)
|
||||||
log.Printf("Starting server on %s", addr)
|
log.Printf("Starting server on %s", addr)
|
||||||
|
|
||||||
// http.Handle("/", http.FileServer(http.Dir(wwwDir)))
|
// Register handlers
|
||||||
// http3.ListenAndServeQUIC("localhost:4242", "/path/to/cert/chain.pem", "/path/to/privkey.pem", nil)
|
registerHandler("hello", handleHello)
|
||||||
|
registerHandler("ping", handlePing)
|
||||||
|
|
||||||
// Set up file server and WebSocket endpoint
|
// Set up file server and WebSocket endpoint
|
||||||
fs := http.FileServer(http.Dir(dir))
|
fs := http.FileServer(http.Dir(dir))
|
||||||
@@ -134,11 +251,11 @@ func main() {
|
|||||||
http.HandleFunc("/ws", handleWebSocket)
|
http.HandleFunc("/ws", handleWebSocket)
|
||||||
|
|
||||||
// Configure and start the HTTP server
|
// Configure and start the HTTP server
|
||||||
server := &http3.Server{
|
server := &http.Server{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
Handler: nil, // nil uses the default ServeMux, which we configured above
|
Handler: nil, // nil uses the default ServeMux, which we configured above
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Server is configured and serving on port %d...", port)
|
log.Printf("Server is configured and serving on port %d...", port)
|
||||||
log.Fatal(server.ListenAndServeTLS("./fullchain.pem", "./privkey.pem"))
|
log.Fatal(server.ListenAndServeTLS("/etc/letsencrypt/live/ddlion.net/fullchain.pem", "/etc/letsencrypt/live/ddlion.net/privkey.pem"))
|
||||||
}
|
}
|
||||||
|
|||||||
783
main.js
783
main.js
@@ -1,4 +1,4 @@
|
|||||||
import { getData, addData, addDataArray } from "./db.js";
|
import { getData, addData, addDataArray, clearData, deleteData } from "./db.js";
|
||||||
// let posts:any;
|
// let posts:any;
|
||||||
// let keyBase = "dandelion_posts_v1_"
|
// let keyBase = "dandelion_posts_v1_"
|
||||||
// let key:string = "";
|
// let key:string = "";
|
||||||
@@ -10,6 +10,31 @@ import { getData, addData, addDataArray } from "./db.js";
|
|||||||
// minute: number,
|
// minute: number,
|
||||||
// second: number,
|
// second: number,
|
||||||
// }
|
// }
|
||||||
|
function waitMs(durationMs) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, durationMs));
|
||||||
|
}
|
||||||
|
function uuidv4() {
|
||||||
|
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (c) => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16));
|
||||||
|
}
|
||||||
|
let logLines = [];
|
||||||
|
let logLength = 10;
|
||||||
|
function log(message) {
|
||||||
|
logLines.push(`${new Date().toLocaleTimeString()}: ${message}`);
|
||||||
|
if (logLines.length > 10) {
|
||||||
|
logLines = logLines.slice(logLines.length - logLength);
|
||||||
|
}
|
||||||
|
let log = document.getElementById("log");
|
||||||
|
if (!log) {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
log.innerText = logLines.join("\n");
|
||||||
|
}
|
||||||
|
function generateID() {
|
||||||
|
if (self.crypto.hasOwnProperty("randomUUID")) {
|
||||||
|
return self.crypto.randomUUID();
|
||||||
|
}
|
||||||
|
return uuidv4();
|
||||||
|
}
|
||||||
class Post {
|
class Post {
|
||||||
constructor(author, author_id, text, post_timestamp, imageData = null, importedFrom = null, importSource = null) {
|
constructor(author, author_id, text, post_timestamp, imageData = null, importedFrom = null, importSource = null) {
|
||||||
this.post_timestamp = post_timestamp;
|
this.post_timestamp = post_timestamp;
|
||||||
@@ -34,319 +59,477 @@ window.addEventListener('scroll', () => {
|
|||||||
// You can perform your action here
|
// You can perform your action here
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
function initMarkdown() {
|
// let connectionReply = await wsConnection.send('hello');
|
||||||
const renderer = new marked.Renderer();
|
// for (let peeer of connectionReply) {
|
||||||
renderer.link = (href, title, text) => {
|
// let peerConnection = await wsConnection.send('connect', peer.id);
|
||||||
return `<a href="${href}" target="_blank"${title ? ` title="${title}"` : ''}>${text}</a>`;
|
// if (peerConnection) {
|
||||||
};
|
// this.peers.push(peerConnection);
|
||||||
marked.setOptions({ renderer: renderer });
|
// let postIDs = await peerConnection.getPostIDs();
|
||||||
}
|
// let postsWeDontHave = this.diffPostIDs(postIDs);
|
||||||
function waitMs(durationMs) {
|
// let newPosts = await peerConnection.getPosts(postsWeDontHave);
|
||||||
return new Promise(resolve => setTimeout(resolve, durationMs));
|
// this.addPosts(newPosts);
|
||||||
}
|
// }
|
||||||
function uuidv4() {
|
// }
|
||||||
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (c) => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16));
|
class wsConnection {
|
||||||
}
|
connect() {
|
||||||
let logLines = [];
|
if (this.websocket?.readyState === WebSocket.OPEN) {
|
||||||
let logLength = 10;
|
return;
|
||||||
function log(message) {
|
|
||||||
logLines.push(`${new Date().toLocaleTimeString()}: ${message}`);
|
|
||||||
if (logLines.length > 10) {
|
|
||||||
logLines = logLines.slice(logLines.length - logLength);
|
|
||||||
}
|
|
||||||
let log = document.getElementById("log");
|
|
||||||
if (!log) {
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
log.innerText = logLines.join("\n");
|
|
||||||
}
|
|
||||||
function arrayBufferToBase64(buffer) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const blob = new Blob([buffer], { type: 'application/octet-stream' });
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onloadend = () => {
|
|
||||||
const dataUrl = reader.result;
|
|
||||||
if (!dataUrl) {
|
|
||||||
resolve(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const base64 = dataUrl.split(',')[1];
|
|
||||||
resolve(base64);
|
|
||||||
};
|
|
||||||
reader.onerror = (error) => {
|
|
||||||
reject(error);
|
|
||||||
};
|
|
||||||
reader.readAsDataURL(blob);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
async function createTestData() {
|
|
||||||
let postsTestData = await (await fetch("./postsTestData.json")).json();
|
|
||||||
return postsTestData;
|
|
||||||
}
|
|
||||||
let time = 0;
|
|
||||||
function timerStart() {
|
|
||||||
time = performance.now();
|
|
||||||
}
|
|
||||||
function timerDelta() {
|
|
||||||
return performance.now() - time;
|
|
||||||
}
|
|
||||||
async function getFixedTweetText(entry) {
|
|
||||||
let fullText = entry.tweet.full_text;
|
|
||||||
let linkMarkdown = "";
|
|
||||||
for (const url of entry.tweet.entities.urls) {
|
|
||||||
linkMarkdown = `[${url.display_url}](${url.expanded_url})`;
|
|
||||||
fullText = fullText.replace(url.url, linkMarkdown);
|
|
||||||
}
|
|
||||||
return fullText;
|
|
||||||
}
|
|
||||||
async function createTestData2(userID) {
|
|
||||||
log("Importing tweet archive");
|
|
||||||
let postsTestData = [];
|
|
||||||
let response = await fetch("./tweets.js");
|
|
||||||
let tweetsText = await response.text();
|
|
||||||
tweetsText = tweetsText.replace("window.YTD.tweets.part0", "window.tweetData");
|
|
||||||
new Function(tweetsText)();
|
|
||||||
// let tweets = JSON.parse(tweetJSON);
|
|
||||||
let count = 0;
|
|
||||||
for (let entry of window.tweetData) {
|
|
||||||
// if (entry.tweet.hasOwnProperty("in_reply_to_screen_name") || entry.tweet.retweeted || entry.tweet.full_text.startsWith("RT")) {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
let mediaURL = entry.tweet?.entities?.media?.[0]?.media_url;
|
|
||||||
let isImage = false;
|
|
||||||
if (mediaURL) {
|
|
||||||
isImage = mediaURL.includes('jpg');
|
|
||||||
}
|
}
|
||||||
let imageData = null;
|
window.clearInterval(this.websocketPingInterval);
|
||||||
if (isImage) {
|
if (this.websocket) {
|
||||||
try {
|
this.websocket.close();
|
||||||
let response = await fetch(mediaURL);
|
}
|
||||||
await waitMs(100);
|
;
|
||||||
if (response.status === 200) {
|
try {
|
||||||
imageData = await response.arrayBuffer();
|
this.websocket = new WebSocket(`wss://${window.location.hostname}:${window.location.port}/ws`);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.websocket.onopen = (evt) => {
|
||||||
|
log("ws:connected");
|
||||||
|
this.websocket.send(`{"type":"hello", "user_id": "${this.userID}", "peer_id":"${this.peerID}"}`);
|
||||||
|
this.websocketPingInterval = window.setInterval(() => {
|
||||||
|
if (!navigator.onLine) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
console.log(imageData);
|
this.websocket.send(`{"type":"ping", "peer_id": "${this.peerID}"}`);
|
||||||
}
|
}, 10000);
|
||||||
catch (e) {
|
};
|
||||||
console.log(e);
|
this.websocket.onclose = (evt) => {
|
||||||
}
|
log("ws:disconnected");
|
||||||
|
// this.retry *= 2;
|
||||||
|
log(`Retrying in ${this.retry} seconds`);
|
||||||
|
window.setTimeout(() => { this.connect(); }, this.retry * 1000);
|
||||||
|
};
|
||||||
|
this.websocket.onmessage = (event) => {
|
||||||
|
log('ws:response: ' + event.data);
|
||||||
|
};
|
||||||
|
this.websocket.onerror = (event) => {
|
||||||
|
log('ws:error: ' + event);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
disconnect() {
|
||||||
|
this.websocket?.close();
|
||||||
|
}
|
||||||
|
constructor(userID, peerID) {
|
||||||
|
this.websocket = null;
|
||||||
|
this.userID = "";
|
||||||
|
this.peerID = "";
|
||||||
|
this.websocketPingInterval = 0;
|
||||||
|
this.retry = 10;
|
||||||
|
this.state = 'disconnected';
|
||||||
|
this.userID = userID;
|
||||||
|
this.peerID = peerID;
|
||||||
|
this.connect();
|
||||||
|
if (!this.websocket) {
|
||||||
|
// set a timer and retry?
|
||||||
}
|
}
|
||||||
let timeStamp = new Date(entry.tweet.created_at);
|
}
|
||||||
let tweetText = await getFixedTweetText(entry);
|
}
|
||||||
let newPost = new Post('bobbydigitales', userID, tweetText, timeStamp, imageData, 'twitter', entry);
|
// function connectWebsocket(userID: string) {
|
||||||
postsTestData.push(newPost);
|
// let websocket = new WebSocket(`ws://${window.location.hostname}:${window.location.port}/ws`);
|
||||||
count++;
|
// websocket.onopen = function (evt) {
|
||||||
if (count % 100 === 0) {
|
// log("Websocket: CONNECTED");
|
||||||
log(`Imported ${count} posts...`);
|
// websocket.send(`{"messageType":"connect", "id": "${userID}"}`);
|
||||||
// render(postsTestData);
|
// let websocketPingInterval = window.setInterval(() => { websocket.send(`{"messageType":"ping", "id": "${userID}"}`); }, 5000)
|
||||||
|
// };
|
||||||
|
// websocket.onclose = function (evt) {
|
||||||
|
// log("Websocket: DISCONNECTED");
|
||||||
|
// };
|
||||||
|
// websocket.onmessage = function (evt) {
|
||||||
|
// log('Websocket: RESPONSE: ' + evt.data);
|
||||||
|
// };
|
||||||
|
// websocket.onerror = function (evt) {
|
||||||
|
// log('Websocket: ERROR: ' + evt);
|
||||||
|
// };
|
||||||
|
// return websocket;
|
||||||
|
// }
|
||||||
|
class App {
|
||||||
|
constructor() {
|
||||||
|
this.userID = '';
|
||||||
|
this.peerID = '';
|
||||||
|
this.time = 0;
|
||||||
|
}
|
||||||
|
initMarkdown() {
|
||||||
|
const renderer = new marked.Renderer();
|
||||||
|
renderer.link = (href, title, text) => {
|
||||||
|
return `<a href="${href}" target="_blank"${title ? ` title="${title}"` : ''}>${text}</a>`;
|
||||||
|
};
|
||||||
|
marked.setOptions({ renderer: renderer });
|
||||||
|
}
|
||||||
|
arrayBufferToBase64(buffer) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const blob = new Blob([buffer], { type: 'application/octet-stream' });
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onloadend = () => {
|
||||||
|
const dataUrl = reader.result;
|
||||||
|
if (!dataUrl) {
|
||||||
|
resolve(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const base64 = dataUrl.split(',')[1];
|
||||||
|
resolve(base64);
|
||||||
|
};
|
||||||
|
reader.onerror = (error) => {
|
||||||
|
reject(error);
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async createTestData() {
|
||||||
|
let postsTestData = await (await fetch("./postsTestData.json")).json();
|
||||||
|
return postsTestData;
|
||||||
|
}
|
||||||
|
timerStart() {
|
||||||
|
this.time = performance.now();
|
||||||
|
}
|
||||||
|
timerDelta() {
|
||||||
|
return performance.now() - this.time;
|
||||||
|
}
|
||||||
|
getFixedTweetText(entry) {
|
||||||
|
let fullText = entry.tweet.full_text;
|
||||||
|
let linkMarkdown = "";
|
||||||
|
for (const url of entry.tweet.entities.urls) {
|
||||||
|
linkMarkdown = `[${url.display_url}](${url.expanded_url})`;
|
||||||
|
fullText = fullText.replace(url.url, linkMarkdown);
|
||||||
}
|
}
|
||||||
// if (count == 100-1) {
|
return fullText;
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
return postsTestData;
|
async importTweetArchive(userID, tweetArchive) {
|
||||||
}
|
log("Importing tweet archive");
|
||||||
async function createTestData3(userID) {
|
let postsTestData = [];
|
||||||
let posts = await (await (fetch('./posts.json'))).json();
|
// let response = await fetch("./tweets.js");
|
||||||
return posts;
|
// let tweetsText = await response.text();
|
||||||
}
|
// tweetsText = tweetsText.replace("window.YTD.tweets.part0", "window.tweetData");
|
||||||
async function registerServiceWorker() {
|
// new Function(tweetsText)();
|
||||||
if (!("serviceWorker" in navigator)) {
|
// let tweets = JSON.parse(tweetJSON);
|
||||||
return;
|
let count = 0;
|
||||||
|
for (let entry of tweetArchive) {
|
||||||
|
if (entry.tweet.hasOwnProperty("in_reply_to_screen_name") || entry.tweet.retweeted || entry.tweet.full_text.startsWith("RT")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mediaURL = entry.tweet?.entities?.media?.[0]?.media_url_https;
|
||||||
|
let isImage = false;
|
||||||
|
if (mediaURL) {
|
||||||
|
isImage = mediaURL.includes('jpg');
|
||||||
|
}
|
||||||
|
let imageData = null;
|
||||||
|
// if (isImage) {
|
||||||
|
// try {
|
||||||
|
// let response = await fetch(mediaURL);
|
||||||
|
// await waitMs(100);
|
||||||
|
// if (response.status === 200) {
|
||||||
|
// imageData = await response.arrayBuffer();
|
||||||
|
// }
|
||||||
|
// console.log(imageData);
|
||||||
|
// } catch (e) {
|
||||||
|
// console.log(e);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
let timeStamp = new Date(entry.tweet.created_at);
|
||||||
|
let tweetText = this.getFixedTweetText(entry);
|
||||||
|
let newPost = new Post('bobbydigitales', userID, tweetText, timeStamp, imageData, 'twitter', entry);
|
||||||
|
postsTestData.push(newPost);
|
||||||
|
count++;
|
||||||
|
if (count % 100 === 0) {
|
||||||
|
log(`Imported ${count} posts...`);
|
||||||
|
// render(postsTestData);
|
||||||
|
}
|
||||||
|
// if (count == 100-1) {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
return postsTestData;
|
||||||
}
|
}
|
||||||
let registrations = await navigator.serviceWorker.getRegistrations();
|
async createTestData3(userID) {
|
||||||
if (registrations.length > 0) {
|
let posts = await (await (fetch('./posts.json'))).json();
|
||||||
console.log("Service worker already registered.");
|
|
||||||
return registrations[0];
|
|
||||||
}
|
|
||||||
navigator.serviceWorker
|
|
||||||
.register("/sw.js")
|
|
||||||
.then((registration) => {
|
|
||||||
console.log("Service Worker registered with scope:", registration.scope);
|
|
||||||
return registration;
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error("Service Worker registration failed:", error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function addPost(userID, posts, postText) {
|
|
||||||
if ((typeof postText !== "string") || postText.length === 0) {
|
|
||||||
log("Not posting an empty string...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let post = new Post(`bobbydigitales`, userID, postText, new Date());
|
|
||||||
posts.push(post);
|
|
||||||
// localStorage.setItem(key, JSON.stringify(posts));
|
|
||||||
addData(userID, post);
|
|
||||||
render(posts);
|
|
||||||
}
|
|
||||||
function generateID() {
|
|
||||||
if (self.crypto.hasOwnProperty("randomUUID")) {
|
|
||||||
return self.crypto.randomUUID();
|
|
||||||
}
|
|
||||||
return uuidv4();
|
|
||||||
}
|
|
||||||
function getUserID() {
|
|
||||||
let id = localStorage.getItem("dandelion_id");
|
|
||||||
if (!id) {
|
|
||||||
id = generateID();
|
|
||||||
localStorage.setItem("dandelion_id", id);
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
function connectWebsocket(userID) {
|
|
||||||
let websocket = new WebSocket(`ws://${window.location.hostname}:${window.location.port}/ws`);
|
|
||||||
websocket.onopen = function (evt) {
|
|
||||||
log("Websocket: CONNECTED");
|
|
||||||
websocket.send(`{"messageType":"connect", "id": "${userID}"}`);
|
|
||||||
let websocketPingInterval = window.setInterval(() => { websocket.send(`{"messageType":"ping", "id": "${userID}"}`); }, 5000);
|
|
||||||
};
|
|
||||||
websocket.onclose = function (evt) {
|
|
||||||
log("Websocket: DISCONNECTED");
|
|
||||||
};
|
|
||||||
websocket.onmessage = function (evt) {
|
|
||||||
log('Websocket: RESPONSE: ' + evt.data);
|
|
||||||
};
|
|
||||||
websocket.onerror = function (evt) {
|
|
||||||
log('Websocket: ERROR: ' + evt);
|
|
||||||
};
|
|
||||||
return websocket;
|
|
||||||
}
|
|
||||||
function setFont(fontName, fontSize) {
|
|
||||||
let content = document.getElementById('content');
|
|
||||||
if (!content) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
content.style.fontFamily = fontName;
|
|
||||||
content.style.fontSize = fontSize;
|
|
||||||
let textArea = document.getElementById('textarea_post');
|
|
||||||
if (!textArea) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
textArea.style.fontFamily = fontName;
|
|
||||||
textArea.style.fontSize = fontSize;
|
|
||||||
}
|
|
||||||
function initOffline() {
|
|
||||||
// Event listener for going offline
|
|
||||||
window.addEventListener('offline', () => { log("offline"); });
|
|
||||||
// Event listener for going online
|
|
||||||
window.addEventListener('online', () => { log("online"); });
|
|
||||||
log(`Online status: ${navigator.onLine ? "online" : "offline"}`);
|
|
||||||
}
|
|
||||||
function initButtons(userID, posts) {
|
|
||||||
let font1Button = document.getElementById("button_font1");
|
|
||||||
let font2Button = document.getElementById("button_font2");
|
|
||||||
font1Button.addEventListener('click', () => { setFont('Bookerly', '16px'); });
|
|
||||||
font2Button.addEventListener('click', () => { setFont('Virgil', '24px'); });
|
|
||||||
let postButton = document.getElementById("button_post");
|
|
||||||
let postText = document.getElementById("textarea_post");
|
|
||||||
if (!(postButton && postText)) {
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
postButton.addEventListener("click", () => {
|
|
||||||
addPost(userID, posts, postText.value);
|
|
||||||
postText.value = "";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
async function loadPosts(userID) {
|
|
||||||
timerStart();
|
|
||||||
let posts = await getData(userID, new Date(2022, 8), new Date());
|
|
||||||
if (posts.length > 0) {
|
|
||||||
log(`Loaded ${posts.length} posts in ${timerDelta().toFixed(2)}ms`);
|
|
||||||
return posts;
|
return posts;
|
||||||
}
|
}
|
||||||
posts = await createTestData2(userID);
|
async registerServiceWorker() {
|
||||||
log("Adding test data...");
|
if (!("serviceWorker" in navigator)) {
|
||||||
addDataArray(userID, posts);
|
return;
|
||||||
return await getData(userID, new Date(2022, 8), new Date());
|
|
||||||
}
|
|
||||||
async function main() {
|
|
||||||
let posts = [];
|
|
||||||
let time = 0;
|
|
||||||
``;
|
|
||||||
let delta = 0;
|
|
||||||
let urlParams = (new URL(window.location.href)).searchParams;
|
|
||||||
if (urlParams.get("sw") === "true") {
|
|
||||||
let registration = await registerServiceWorker();
|
|
||||||
}
|
|
||||||
let userID = getUserID();
|
|
||||||
log(`Your user ID is: ${userID}`);
|
|
||||||
if (navigator.storage && navigator.storage.persist && !navigator.storage.persisted) {
|
|
||||||
const isPersisted = await navigator.storage.persist();
|
|
||||||
log(`Persisted storage granted: ${isPersisted}`);
|
|
||||||
}
|
|
||||||
log(`Persisted: ${(await navigator?.storage?.persisted())?.toString()}`);
|
|
||||||
initMarkdown();
|
|
||||||
// let main = await fetch("/main.js");
|
|
||||||
// let code = await main.text();
|
|
||||||
// console.log(code);
|
|
||||||
// registration.active.postMessage({type:"updateMain", code:code});
|
|
||||||
posts = await loadPosts(userID);
|
|
||||||
let websocket = connectWebsocket(userID);
|
|
||||||
initOffline();
|
|
||||||
initButtons(userID, posts);
|
|
||||||
// debugger;
|
|
||||||
timerStart();
|
|
||||||
render(posts);
|
|
||||||
let renderTime = timerDelta();
|
|
||||||
log(`render took: ${renderTime.toFixed(2)}ms`);
|
|
||||||
log(`memory used: ${(performance.memory.usedJSHeapSize / 1024 / 1024).toFixed(2)}Mb`);
|
|
||||||
}
|
|
||||||
function render(posts) {
|
|
||||||
const fragment = document.createDocumentFragment();
|
|
||||||
let contentDiv = document.getElementById("content");
|
|
||||||
if (!contentDiv) {
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
contentDiv.innerHTML = "";
|
|
||||||
let count = 0;
|
|
||||||
for (let i = posts.length - 1; i >= 0; i--) {
|
|
||||||
let postData = posts[i];
|
|
||||||
let post = renderPost(postData);
|
|
||||||
if (post) {
|
|
||||||
fragment.appendChild(post);
|
|
||||||
count++;
|
|
||||||
}
|
}
|
||||||
if (count > 100) {
|
let registrations = await navigator.serviceWorker.getRegistrations();
|
||||||
break;
|
if (registrations.length > 0) {
|
||||||
|
console.log("Service worker already registered.");
|
||||||
|
return registrations[0];
|
||||||
}
|
}
|
||||||
|
navigator.serviceWorker
|
||||||
|
.register("/sw.js")
|
||||||
|
.then((registration) => {
|
||||||
|
console.log("Service Worker registered with scope:", registration.scope);
|
||||||
|
return registration;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Service Worker registration failed:", error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (!contentDiv) {
|
addPost(userID, posts, postText) {
|
||||||
throw new Error("Couldn't get content div!");
|
if ((typeof postText !== "string") || postText.length === 0) {
|
||||||
|
log("Not posting an empty string...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let post = new Post(`bobbydigitales`, userID, postText, new Date());
|
||||||
|
posts.push(post);
|
||||||
|
// localStorage.setItem(key, JSON.stringify(posts));
|
||||||
|
addData(userID, post);
|
||||||
|
this.render(posts);
|
||||||
}
|
}
|
||||||
contentDiv.appendChild(fragment);
|
getPeerID() {
|
||||||
}
|
let id = localStorage.getItem("peer_id");
|
||||||
function renderPost(post) {
|
if (!id) {
|
||||||
if (!(post.hasOwnProperty("text"))) {
|
id = generateID();
|
||||||
throw new Error("Post is malformed!");
|
localStorage.setItem("peer_id", id);
|
||||||
|
}
|
||||||
|
return id;
|
||||||
}
|
}
|
||||||
let containerDiv = document.createElement("div");
|
getUserID() {
|
||||||
let textDiv = document.createElement("div");
|
let id = localStorage.getItem("dandelion_id");
|
||||||
let timestampDiv = document.createElement("div");
|
if (!id) {
|
||||||
let hr = document.createElement("hr");
|
id = generateID();
|
||||||
textDiv.innerHTML = marked.parse(post.text);
|
localStorage.setItem("dandelion_id", id);
|
||||||
// textDiv.innerHTML = DOMPurify.sanitize(marked.parse(post.text));
|
}
|
||||||
timestampDiv.innerText = `${post.post_timestamp.toDateString()}`;
|
return id;
|
||||||
timestampDiv.title = `${post.post_timestamp.toLocaleTimeString()} · ${post.post_timestamp.toDateString()}`;
|
}
|
||||||
containerDiv.appendChild(hr);
|
setFont(fontName, fontSize) {
|
||||||
containerDiv.appendChild(textDiv);
|
let content = document.getElementById('content');
|
||||||
if (!("image_data" in post && post.image_data)) {
|
if (!content) {
|
||||||
containerDiv.appendChild(timestampDiv);
|
return;
|
||||||
|
}
|
||||||
|
content.style.fontFamily = fontName;
|
||||||
|
content.style.fontSize = fontSize;
|
||||||
|
let textArea = document.getElementById('textarea_post');
|
||||||
|
if (!textArea) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
textArea.style.fontFamily = fontName;
|
||||||
|
textArea.style.fontSize = fontSize;
|
||||||
|
}
|
||||||
|
initOffline(connection) {
|
||||||
|
// Event listener for going offline
|
||||||
|
window.addEventListener('offline', () => {
|
||||||
|
log("offline");
|
||||||
|
});
|
||||||
|
// Event listener for going online
|
||||||
|
window.addEventListener('online', () => {
|
||||||
|
log("online");
|
||||||
|
connection.connect();
|
||||||
|
});
|
||||||
|
log(`Online status: ${navigator.onLine ? "online" : "offline"}`);
|
||||||
|
}
|
||||||
|
selectFile(contentType) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
let input = document.createElement('input');
|
||||||
|
input.type = 'file';
|
||||||
|
// input.multiple = multiple;
|
||||||
|
input.accept = contentType;
|
||||||
|
input.onchange = () => {
|
||||||
|
if (input.files == null) {
|
||||||
|
resolve(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let files = Array.from(input.files);
|
||||||
|
// if (multiple)
|
||||||
|
// resolve(files);
|
||||||
|
// else
|
||||||
|
resolve(files[0]);
|
||||||
|
};
|
||||||
|
input.click();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
readFile(file) {
|
||||||
|
// Always return a Promise
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let content = '';
|
||||||
|
const reader = new FileReader();
|
||||||
|
// Wait till complete
|
||||||
|
reader.onloadend = function (e) {
|
||||||
|
content = e.target.result;
|
||||||
|
resolve(content);
|
||||||
|
};
|
||||||
|
// Make sure to handle error states
|
||||||
|
reader.onerror = function (e) {
|
||||||
|
reject(e);
|
||||||
|
};
|
||||||
|
reader.readAsText(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
initButtons(userID, posts, registration) {
|
||||||
|
let font1Button = document.getElementById("button_font1");
|
||||||
|
let font2Button = document.getElementById("button_font2");
|
||||||
|
let importTweetsButton = document.getElementById("import_tweets");
|
||||||
|
let clearPostsButton = document.getElementById("clear_posts");
|
||||||
|
let updateApp = document.getElementById("update_app");
|
||||||
|
let ddlnLogoButton = document.getElementById('ddln-logo-button');
|
||||||
|
font1Button.addEventListener('click', () => { this.setFont('Bookerly', '16px'); });
|
||||||
|
font2Button.addEventListener('click', () => { this.setFont('Virgil', '16px'); });
|
||||||
|
importTweetsButton.addEventListener('click', async () => {
|
||||||
|
let file = await this.selectFile('text/*');
|
||||||
|
console.log(file);
|
||||||
|
if (file == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let tweetData = await this.readFile(file);
|
||||||
|
tweetData = tweetData.replace('window.YTD.tweets.part0 = ', '');
|
||||||
|
const tweets = JSON.parse(tweetData);
|
||||||
|
let imported_posts = await this.importTweetArchive(userID, tweets);
|
||||||
|
clearData(userID);
|
||||||
|
// posts = posts.reverse();
|
||||||
|
addDataArray(userID, imported_posts);
|
||||||
|
posts = await this.loadPosts(userID) ?? [];
|
||||||
|
this.render(posts);
|
||||||
|
});
|
||||||
|
clearPostsButton.addEventListener('click', () => { clearData(userID); posts = []; this.render(posts); });
|
||||||
|
let postButton = document.getElementById("button_post");
|
||||||
|
let postText = document.getElementById("textarea_post");
|
||||||
|
if (!(postButton && postText)) {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
postButton.addEventListener("click", () => {
|
||||||
|
this.addPost(userID, posts, postText.value);
|
||||||
|
postText.value = "";
|
||||||
|
});
|
||||||
|
updateApp.addEventListener("click", () => {
|
||||||
|
registration?.active?.postMessage({ type: "update_app" });
|
||||||
|
});
|
||||||
|
let infoElement = document.getElementById('info');
|
||||||
|
if (infoElement === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ddlnLogoButton.addEventListener('click', () => { infoElement.style.display == 'none' ? infoElement.style.display = 'block' : infoElement.style.display = 'none'; });
|
||||||
|
}
|
||||||
|
async loadPosts(userID) {
|
||||||
|
this.timerStart();
|
||||||
|
let posts = await getData(userID, new Date(2022, 8), new Date());
|
||||||
|
if (posts.length > 0) {
|
||||||
|
log(`Loaded ${posts.length} posts in ${this.timerDelta().toFixed(2)}ms`);
|
||||||
|
return posts;
|
||||||
|
}
|
||||||
|
// posts = await createTestData2(userID);
|
||||||
|
// log("Adding test data...");
|
||||||
|
// addDataArray(userID, posts);
|
||||||
|
// return await getData(userID, new Date(2022, 8), new Date());
|
||||||
|
}
|
||||||
|
async main() {
|
||||||
|
let urlParams = (new URL(window.location.href)).searchParams;
|
||||||
|
let connection_userID = urlParams.get('connect');
|
||||||
|
let registration = undefined;
|
||||||
|
if (urlParams.get("sw") === "true") {
|
||||||
|
registration = await this.registerServiceWorker();
|
||||||
|
}
|
||||||
|
if (connection_userID) {
|
||||||
|
console.log('connect', connection_userID);
|
||||||
|
localStorage.setItem("dandelion_id", connection_userID);
|
||||||
|
}
|
||||||
|
let posts = [];
|
||||||
|
let userID = this.getUserID();
|
||||||
|
let peerID = this.getPeerID();
|
||||||
|
this.userID = userID;
|
||||||
|
this.peerID = peerID;
|
||||||
|
log(`user:${userID} peer:${peerID}`);
|
||||||
|
let websocket = new wsConnection(userID, peerID);
|
||||||
|
window.addEventListener('beforeunload', () => { websocket.disconnect(); });
|
||||||
|
this.initOffline(websocket);
|
||||||
|
this.initButtons(userID, posts, registration);
|
||||||
|
let time = 0;
|
||||||
|
let delta = 0;
|
||||||
|
if (navigator.storage && navigator.storage.persist && !navigator.storage.persisted) {
|
||||||
|
debugger;
|
||||||
|
const isPersisted = await navigator.storage.persist();
|
||||||
|
log(`Persisted storage granted: ${isPersisted}`);
|
||||||
|
}
|
||||||
|
log(`Persisted: ${(await navigator?.storage?.persisted())?.toString()}`);
|
||||||
|
this.initMarkdown();
|
||||||
|
// let main = await fetch("/main.js");
|
||||||
|
// let code = await main.text();
|
||||||
|
// console.log(code);
|
||||||
|
// registration.active.postMessage({type:"updateMain", code:code});
|
||||||
|
posts = await this.loadPosts(userID) ?? [];
|
||||||
|
// debugger;
|
||||||
|
this.timerStart();
|
||||||
|
this.render(posts); // , (postID:string)=>{this.deletePost(userID, postID)}
|
||||||
|
let renderTime = this.timerDelta();
|
||||||
|
log(`render took: ${renderTime.toFixed(2)}ms`);
|
||||||
|
if (performance?.memory) {
|
||||||
|
log(`memory used: ${(performance.memory.usedJSHeapSize / 1024 / 1024).toFixed(2)}Mb`);
|
||||||
|
}
|
||||||
|
// const client = new WebTorrent()
|
||||||
|
// // Sintel, a free, Creative Commons movie
|
||||||
|
// const torrentId = 'magnet:?xt=urn:btih:6091e199a8d9272a40dd9a25a621a5c355d6b0be&dn=WING+IT!+-+Blender+Open+Movie+1080p.mp4&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&tr=udp%3A%2F%2Fexplodie.org%3A6969&tr=udp%3A%2F%2Ftracker.empire-js.us%3A1337';
|
||||||
|
// client.add(torrentId, function (torrent: any) {
|
||||||
|
// // Torrents can contain many files. Let's use the .mp4 file
|
||||||
|
// const file = torrent.files.find(function (file: any) {
|
||||||
|
// return file.name.endsWith('.mp4')
|
||||||
|
// })
|
||||||
|
// // Display the file by adding it to the DOM.
|
||||||
|
// // Supports video, audio, image files, and more!
|
||||||
|
// file.appendTo(document.getElementById('torrent-content'));
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
render(posts) {
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
let contentDiv = document.getElementById("content");
|
||||||
|
if (!contentDiv) {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
contentDiv.innerHTML = "";
|
||||||
|
let count = 0;
|
||||||
|
new QRCode(document.getElementById('qrcode'), `https://ddlion.net/?connect=${this.userID}`);
|
||||||
|
for (let i = posts.length - 1; i >= 0; i--) {
|
||||||
|
let postData = posts[i];
|
||||||
|
let post = this.renderPost(postData, posts);
|
||||||
|
if (post) {
|
||||||
|
fragment.appendChild(post);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
if (count > 100) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!contentDiv) {
|
||||||
|
throw new Error("Couldn't get content div!");
|
||||||
|
}
|
||||||
|
contentDiv.appendChild(fragment);
|
||||||
|
}
|
||||||
|
deletePost(userID, postID) {
|
||||||
|
deleteData(userID, postID);
|
||||||
|
}
|
||||||
|
renderPost(post, posts) {
|
||||||
|
if (!(post.hasOwnProperty("text"))) {
|
||||||
|
throw new Error("Post is malformed!");
|
||||||
|
}
|
||||||
|
let containerDiv = document.createElement("div");
|
||||||
|
let timestamp = `${post.post_timestamp.toLocaleTimeString()} · ${post.post_timestamp.toLocaleDateString()}`;
|
||||||
|
let deleteButton = document.createElement('button');
|
||||||
|
deleteButton.innerText = 'delete';
|
||||||
|
// deleteButton.onclick = ()=>{deletefunc(post.post_id)};
|
||||||
|
let postTemplate = `<div><hr>
|
||||||
|
<div><span class='header' title='${timestamp}'>@${post.author} - ${post.post_timestamp.toLocaleDateString()}</span></div>
|
||||||
|
<div>${marked.parse(post.text)}</div>
|
||||||
|
</div>`;
|
||||||
|
containerDiv.innerHTML = postTemplate;
|
||||||
|
containerDiv.appendChild(deleteButton);
|
||||||
|
// if (!("image_data" in post && post.image_data)) {
|
||||||
|
// containerDiv.appendChild(timestampDiv);
|
||||||
|
// return containerDiv;
|
||||||
|
// // return null;
|
||||||
|
// }
|
||||||
|
// let image = document.createElement("img");
|
||||||
|
// const blob = new Blob([post.image_data as ArrayBuffer], { type: 'image/jpg' });
|
||||||
|
// const url = URL.createObjectURL(blob);
|
||||||
|
// image.onload = () => {
|
||||||
|
// URL.revokeObjectURL(url);
|
||||||
|
// };
|
||||||
|
// image.src = url;
|
||||||
|
// // image.src = image.src = "data:image/png;base64," + post.image;
|
||||||
|
// image.className = "postImage";
|
||||||
|
// containerDiv.appendChild(image);
|
||||||
|
// containerDiv.appendChild(timestampDiv);
|
||||||
return containerDiv;
|
return containerDiv;
|
||||||
// return null;
|
|
||||||
}
|
}
|
||||||
let image = document.createElement("img");
|
|
||||||
const blob = new Blob([post.image_data], { type: 'image/jpg' });
|
|
||||||
const url = URL.createObjectURL(blob);
|
|
||||||
image.onload = () => {
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
};
|
|
||||||
image.src = url;
|
|
||||||
// image.src = image.src = "data:image/png;base64," + post.image;
|
|
||||||
image.className = "postImage";
|
|
||||||
containerDiv.appendChild(image);
|
|
||||||
containerDiv.appendChild(timestampDiv);
|
|
||||||
return containerDiv;
|
|
||||||
}
|
}
|
||||||
window.addEventListener("load", main);
|
let app = new App();
|
||||||
|
window.addEventListener("load", app.main.bind(app));
|
||||||
//# sourceMappingURL=main.js.map
|
//# sourceMappingURL=main.js.map
|
||||||
File diff suppressed because one or more lines are too long
13
mdns/main.js
Normal file
13
mdns/main.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
const bonjour = require("bonjour")();
|
||||||
|
|
||||||
|
const PORT = 6789;
|
||||||
|
|
||||||
|
// Advertise the service using bonjour
|
||||||
|
let result = bonjour.publish({
|
||||||
|
name: "ddln",
|
||||||
|
type: "https",
|
||||||
|
port: PORT,
|
||||||
|
host: "ddln.local",
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(result);
|
||||||
370
mdns/package-lock.json
generated
Normal file
370
mdns/package-lock.json
generated
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
{
|
||||||
|
"name": "mdns",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"dependencies": {
|
||||||
|
"bonjour": "^3.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/array-flatten": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ=="
|
||||||
|
},
|
||||||
|
"node_modules/bonjour": {
|
||||||
|
"version": "3.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
|
||||||
|
"integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==",
|
||||||
|
"dependencies": {
|
||||||
|
"array-flatten": "^2.1.0",
|
||||||
|
"deep-equal": "^1.0.1",
|
||||||
|
"dns-equal": "^1.0.0",
|
||||||
|
"dns-txt": "^2.0.2",
|
||||||
|
"multicast-dns": "^6.0.1",
|
||||||
|
"multicast-dns-service-types": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/buffer-indexof": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g=="
|
||||||
|
},
|
||||||
|
"node_modules/call-bind": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"function-bind": "^1.1.2",
|
||||||
|
"get-intrinsic": "^1.2.1",
|
||||||
|
"set-function-length": "^1.1.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/deep-equal": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-arguments": "^1.0.4",
|
||||||
|
"is-date-object": "^1.0.1",
|
||||||
|
"is-regex": "^1.0.4",
|
||||||
|
"object-is": "^1.0.1",
|
||||||
|
"object-keys": "^1.1.1",
|
||||||
|
"regexp.prototype.flags": "^1.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/define-data-property": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"get-intrinsic": "^1.2.1",
|
||||||
|
"gopd": "^1.0.1",
|
||||||
|
"has-property-descriptors": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/define-properties": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
|
||||||
|
"dependencies": {
|
||||||
|
"define-data-property": "^1.0.1",
|
||||||
|
"has-property-descriptors": "^1.0.0",
|
||||||
|
"object-keys": "^1.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dns-equal": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg=="
|
||||||
|
},
|
||||||
|
"node_modules/dns-packet": {
|
||||||
|
"version": "1.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz",
|
||||||
|
"integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==",
|
||||||
|
"dependencies": {
|
||||||
|
"ip": "^1.1.0",
|
||||||
|
"safe-buffer": "^5.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dns-txt": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"buffer-indexof": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/function-bind": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/functions-have-names": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-intrinsic": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
|
||||||
|
"dependencies": {
|
||||||
|
"function-bind": "^1.1.2",
|
||||||
|
"has-proto": "^1.0.1",
|
||||||
|
"has-symbols": "^1.0.3",
|
||||||
|
"hasown": "^2.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/gopd": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
|
||||||
|
"dependencies": {
|
||||||
|
"get-intrinsic": "^1.1.3"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-property-descriptors": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
|
||||||
|
"dependencies": {
|
||||||
|
"get-intrinsic": "^1.2.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-proto": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-symbols": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-tostringtag": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"has-symbols": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/hasown": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
|
||||||
|
"dependencies": {
|
||||||
|
"function-bind": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ip": {
|
||||||
|
"version": "1.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz",
|
||||||
|
"integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg=="
|
||||||
|
},
|
||||||
|
"node_modules/is-arguments": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.2",
|
||||||
|
"has-tostringtag": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-date-object": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"has-tostringtag": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-regex": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.2",
|
||||||
|
"has-tostringtag": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/multicast-dns": {
|
||||||
|
"version": "6.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz",
|
||||||
|
"integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==",
|
||||||
|
"dependencies": {
|
||||||
|
"dns-packet": "^1.3.1",
|
||||||
|
"thunky": "^1.0.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"multicast-dns": "cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/multicast-dns-service-types": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ=="
|
||||||
|
},
|
||||||
|
"node_modules/object-is": {
|
||||||
|
"version": "1.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
|
||||||
|
"integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.2",
|
||||||
|
"define-properties": "^1.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/object-keys": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/regexp.prototype.flags": {
|
||||||
|
"version": "1.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz",
|
||||||
|
"integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.2",
|
||||||
|
"define-properties": "^1.2.0",
|
||||||
|
"set-function-name": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/safe-buffer": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/set-function-length": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"define-data-property": "^1.1.1",
|
||||||
|
"get-intrinsic": "^1.2.1",
|
||||||
|
"gopd": "^1.0.1",
|
||||||
|
"has-property-descriptors": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/set-function-name": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==",
|
||||||
|
"dependencies": {
|
||||||
|
"define-data-property": "^1.0.1",
|
||||||
|
"functions-have-names": "^1.2.3",
|
||||||
|
"has-property-descriptors": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/thunky": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
mdns/package.json
Normal file
5
mdns/package.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"bonjour": "^3.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5.4.3"
|
"typescript": "^5.5.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
robots.txt
Normal file
2
robots.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
User-agent: *
|
||||||
|
Disallow: /
|
||||||
93
src/db.ts
93
src/db.ts
@@ -4,8 +4,7 @@
|
|||||||
// email: string;
|
// email: string;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
const dbName: string = "ddln";
|
const postStoreName: string = "posts";
|
||||||
const storeNameBase: string = "posts";
|
|
||||||
let keyBase = "dandelion_posts_v1_"
|
let keyBase = "dandelion_posts_v1_"
|
||||||
let key = "";
|
let key = "";
|
||||||
|
|
||||||
@@ -19,8 +18,8 @@ type DBError = Event & {
|
|||||||
target: { errorCode: DOMException };
|
target: { errorCode: DOMException };
|
||||||
};
|
};
|
||||||
|
|
||||||
export function openDatabase(userID:string): Promise<IDBDatabase> {
|
export function openDatabase(userID: string): Promise<IDBDatabase> {
|
||||||
const storeName = `${storeNameBase}_${userID}`;
|
const dbName = `user_${userID}`
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const request: IDBOpenDBRequest = indexedDB.open(dbName, 1);
|
const request: IDBOpenDBRequest = indexedDB.open(dbName, 1);
|
||||||
@@ -33,8 +32,8 @@ export function openDatabase(userID:string): Promise<IDBDatabase> {
|
|||||||
|
|
||||||
request.onupgradeneeded = (event: IDBVersionChangeEvent) => {
|
request.onupgradeneeded = (event: IDBVersionChangeEvent) => {
|
||||||
const db: IDBDatabase = (event.target as IDBOpenDBRequest).result;
|
const db: IDBDatabase = (event.target as IDBOpenDBRequest).result;
|
||||||
if (!db.objectStoreNames.contains(storeName)) {
|
if (!db.objectStoreNames.contains(postStoreName)) {
|
||||||
let store = db.createObjectStore(storeName, { keyPath: "id", autoIncrement: true });
|
let store = db.createObjectStore(postStoreName, { keyPath: "id", autoIncrement: true });
|
||||||
store.createIndex("datetimeIndex", "post_timestamp", { unique: false });
|
store.createIndex("datetimeIndex", "post_timestamp", { unique: false });
|
||||||
store.createIndex("postIDIndex", "data.post_id", { unique: true });
|
store.createIndex("postIDIndex", "data.post_id", { unique: true });
|
||||||
|
|
||||||
@@ -52,12 +51,11 @@ export function openDatabase(userID:string): Promise<IDBDatabase> {
|
|||||||
|
|
||||||
export async function addData(userID: string, data: any): Promise<void> {
|
export async function addData(userID: string, data: any): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const storeName = `${storeNameBase}_${userID}`;
|
|
||||||
const db = await openDatabase(userID);
|
const db = await openDatabase(userID);
|
||||||
const transaction = db.transaction(storeName, "readwrite");
|
const transaction = db.transaction(postStoreName, "readwrite");
|
||||||
const store = transaction.objectStore(storeName);
|
const store = transaction.objectStore(postStoreName);
|
||||||
|
|
||||||
const addRequest = store.add({post_timestamp: data.post_timestamp, data:data});
|
const addRequest = store.add({ post_timestamp: data.post_timestamp, data: data });
|
||||||
|
|
||||||
addRequest.onsuccess = (e: Event) => {
|
addRequest.onsuccess = (e: Event) => {
|
||||||
// console.log('Data has been added:', (e.target as IDBRequest).result);
|
// console.log('Data has been added:', (e.target as IDBRequest).result);
|
||||||
@@ -73,23 +71,72 @@ export async function addData(userID: string, data: any): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function deleteData(userID: string, postID: string) {
|
||||||
|
try {
|
||||||
|
const db = await openDatabase(userID);
|
||||||
|
const transaction = db.transaction(postStoreName, "readwrite");
|
||||||
|
const store = transaction.objectStore(postStoreName);
|
||||||
|
const index = store.index("postIDIndex");
|
||||||
|
|
||||||
|
|
||||||
|
const getRequest = index.getKey(postID);
|
||||||
|
|
||||||
|
getRequest.onerror = e => console.log((e.target as IDBRequest).error)
|
||||||
|
|
||||||
|
getRequest.onsuccess = e => {
|
||||||
|
const key = (e.target as IDBRequest).result;
|
||||||
|
if (key === undefined) {
|
||||||
|
console.error("Post not found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteRequest = store.delete(key);
|
||||||
|
deleteRequest.onerror = e => { console.error((e.target as IDBRequest).error); return false };
|
||||||
|
deleteRequest.onsuccess = () => true;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in opening database:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function clearData(userID: string) {
|
||||||
|
try {
|
||||||
|
const db = await openDatabase(userID);
|
||||||
|
const transaction = db.transaction(postStoreName, "readwrite");
|
||||||
|
const store = transaction.objectStore(postStoreName);
|
||||||
|
|
||||||
|
const clearRequest = store.clear();
|
||||||
|
clearRequest.onsuccess = (e: Event) => {
|
||||||
|
// console.log('Data has been added:', (e.target as IDBRequest).result);
|
||||||
|
};
|
||||||
|
|
||||||
|
clearRequest.onerror = (event: Event) => {
|
||||||
|
// Use a type assertion to access the specific properties of IDBRequest error event
|
||||||
|
const errorEvent = event as IDBRequestEvent;
|
||||||
|
console.error('Error in clearing data:', errorEvent.target.error?.message);
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in opening database:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function addDataArray(userID: string, array: any[]): Promise<void> {
|
export async function addDataArray(userID: string, array: any[]): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const storeName = `${storeNameBase}_${userID}`;
|
|
||||||
const db = await openDatabase(userID);
|
const db = await openDatabase(userID);
|
||||||
const transaction = db.transaction(storeName, "readwrite");
|
const transaction = db.transaction(postStoreName, "readwrite");
|
||||||
const store = transaction.objectStore(storeName);
|
const store = transaction.objectStore(postStoreName);
|
||||||
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
array.reverse();
|
array.reverse();
|
||||||
|
|
||||||
for (let data of array) {
|
for (let data of array) {
|
||||||
const addRequest = store.add({post_timestamp: data.post_timestamp, data:data});
|
const addRequest = store.add({ post_timestamp: data.post_timestamp, data: data });
|
||||||
addRequest.onsuccess = (e: Event) => {
|
addRequest.onsuccess = (e: Event) => {
|
||||||
// console.log('Data has been added:', (e.target as IDBRequest).result);
|
// console.log('Data has been added:', (e.target as IDBRequest).result);
|
||||||
};
|
};
|
||||||
|
|
||||||
addRequest.onerror = (event: Event) => {
|
addRequest.onerror = (event: Event) => {
|
||||||
// Use a type assertion to access the specific properties of IDBRequest error event
|
// Use a type assertion to access the specific properties of IDBRequest error event
|
||||||
const errorEvent = event as IDBRequestEvent;
|
const errorEvent = event as IDBRequestEvent;
|
||||||
@@ -103,17 +150,16 @@ export async function addDataArray(userID: string, array: any[]): Promise<void>
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in opening database:', error);
|
console.error('Error in opening database:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getData(userID:string, lowerID:Date, upperID:Date): Promise<any | undefined> {
|
export async function getData(userID: string, lowerID: Date, upperID: Date): Promise<any | undefined> {
|
||||||
const storeName = `${storeNameBase}_${userID}`;
|
|
||||||
const db = await openDatabase(userID);
|
const db = await openDatabase(userID);
|
||||||
const transaction = db.transaction(storeName, "readonly");
|
const transaction = db.transaction(postStoreName, "readonly");
|
||||||
const store = transaction.objectStore(storeName);
|
const store = transaction.objectStore(postStoreName);
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const keyRangeValue = IDBKeyRange.bound(lowerID, upperID);
|
const keyRangeValue = IDBKeyRange.bound(lowerID, upperID);
|
||||||
@@ -145,11 +191,10 @@ export async function getData(userID:string, lowerID:Date, upperID:Date): Promis
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAllData(userID:string): Promise<any | undefined> {
|
export async function getAllData(userID: string): Promise<any | undefined> {
|
||||||
const storeName = `${storeNameBase}_${userID}`;
|
|
||||||
const db = await openDatabase(userID);
|
const db = await openDatabase(userID);
|
||||||
const transaction = db.transaction(storeName, "readonly");
|
const transaction = db.transaction(postStoreName, "readonly");
|
||||||
const store = transaction.objectStore(storeName);
|
const store = transaction.objectStore(postStoreName);
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const getRequest = store.getAll();
|
const getRequest = store.getAll();
|
||||||
|
|||||||
998
src/main.ts
998
src/main.ts
File diff suppressed because it is too large
Load Diff
83
src/webRTC.ts
Normal file
83
src/webRTC.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
const config = {
|
||||||
|
iceServers: [{ urls: "stun: stun.l.google.com" }],
|
||||||
|
};
|
||||||
|
|
||||||
|
let localConnection = new RTCPeerConnection();
|
||||||
|
|
||||||
|
function handleSendChannelStatusChange() {
|
||||||
|
console.log(handleSendChannelStatusChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
let sendChannel = localConnection.createDataChannel("sendChannel");
|
||||||
|
sendChannel.onopen = handleSendChannelStatusChange;
|
||||||
|
sendChannel.onclose = handleSendChannelStatusChange;
|
||||||
|
|
||||||
|
|
||||||
|
let remoteConnection = new RTCPeerConnection();
|
||||||
|
remoteConnection.ondatachannel = receiveChannelCallback;
|
||||||
|
|
||||||
|
|
||||||
|
localConnection.onicecandidate = (e:any) =>
|
||||||
|
!e.candidate ||
|
||||||
|
remoteConnection.addIceCandidate(e.candidate).catch(handleAddCandidateError);
|
||||||
|
|
||||||
|
remoteConnection.onicecandidate = (e) =>
|
||||||
|
!e.candidate ||
|
||||||
|
localConnection.addIceCandidate(e.candidate).catch(handleAddCandidateError);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function handleCreateDescriptionError(error:any) {
|
||||||
|
console.log(`Unable to create an offer: ${error.toString()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleLocalAddCandidateSuccess() {
|
||||||
|
console.log('handleLocalAddCandidateSuccess');
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRemoteAddCandidateSuccess() {
|
||||||
|
console.log('handleRemoteAddCandidateSuccess');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAddCandidateError() {
|
||||||
|
console.log("Oh noes! addICECandidate failed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
localConnection
|
||||||
|
.createOffer()
|
||||||
|
.then((offer) => localConnection.setLocalDescription(offer))
|
||||||
|
.then(() =>
|
||||||
|
remoteConnection.setRemoteDescription(localConnection.localDescription as RTCSessionDescriptionInit),
|
||||||
|
)
|
||||||
|
.then(() => remoteConnection.createAnswer())
|
||||||
|
.then((answer) => remoteConnection.setLocalDescription(answer))
|
||||||
|
.then(() =>
|
||||||
|
localConnection.setRemoteDescription(remoteConnection.localDescription as RTCSessionDescriptionInit),
|
||||||
|
)
|
||||||
|
.catch(handleCreateDescriptionError);
|
||||||
|
|
||||||
|
function handleReceiveChannelStatusChange(event:any) {
|
||||||
|
let receiveChannel = event.channel;
|
||||||
|
|
||||||
|
if (receiveChannel) {
|
||||||
|
console.log(
|
||||||
|
`Receive channel's status has changed to ${receiveChannel.readyState}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function receiveChannelCallback(event:any) {
|
||||||
|
let receiveChannel = event.channel;
|
||||||
|
receiveChannel.onmessage = handleReceiveMessage;
|
||||||
|
receiveChannel.onopen = handleReceiveChannelStatusChange;
|
||||||
|
receiveChannel.onclose = handleReceiveChannelStatusChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendMessage(message:string) {
|
||||||
|
sendChannel.send(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleReceiveMessage(event:any) {
|
||||||
|
console.log(event.data);
|
||||||
|
}
|
||||||
56
webRTC.js
Normal file
56
webRTC.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
"use strict";
|
||||||
|
const config = {
|
||||||
|
iceServers: [{ urls: "stun: stun.l.google.com" }],
|
||||||
|
};
|
||||||
|
let localConnection = new RTCPeerConnection();
|
||||||
|
function handleSendChannelStatusChange() {
|
||||||
|
console.log(handleSendChannelStatusChange);
|
||||||
|
}
|
||||||
|
let sendChannel = localConnection.createDataChannel("sendChannel");
|
||||||
|
sendChannel.onopen = handleSendChannelStatusChange;
|
||||||
|
sendChannel.onclose = handleSendChannelStatusChange;
|
||||||
|
let remoteConnection = new RTCPeerConnection();
|
||||||
|
remoteConnection.ondatachannel = receiveChannelCallback;
|
||||||
|
localConnection.onicecandidate = (e) => !e.candidate ||
|
||||||
|
remoteConnection.addIceCandidate(e.candidate).catch(handleAddCandidateError);
|
||||||
|
remoteConnection.onicecandidate = (e) => !e.candidate ||
|
||||||
|
localConnection.addIceCandidate(e.candidate).catch(handleAddCandidateError);
|
||||||
|
function handleCreateDescriptionError(error) {
|
||||||
|
console.log(`Unable to create an offer: ${error.toString()}`);
|
||||||
|
}
|
||||||
|
function handleLocalAddCandidateSuccess() {
|
||||||
|
console.log('handleLocalAddCandidateSuccess');
|
||||||
|
}
|
||||||
|
function handleRemoteAddCandidateSuccess() {
|
||||||
|
console.log('handleRemoteAddCandidateSuccess');
|
||||||
|
}
|
||||||
|
function handleAddCandidateError() {
|
||||||
|
console.log("Oh noes! addICECandidate failed!");
|
||||||
|
}
|
||||||
|
localConnection
|
||||||
|
.createOffer()
|
||||||
|
.then((offer) => localConnection.setLocalDescription(offer))
|
||||||
|
.then(() => remoteConnection.setRemoteDescription(localConnection.localDescription))
|
||||||
|
.then(() => remoteConnection.createAnswer())
|
||||||
|
.then((answer) => remoteConnection.setLocalDescription(answer))
|
||||||
|
.then(() => localConnection.setRemoteDescription(remoteConnection.localDescription))
|
||||||
|
.catch(handleCreateDescriptionError);
|
||||||
|
function handleReceiveChannelStatusChange(event) {
|
||||||
|
let receiveChannel = event.channel;
|
||||||
|
if (receiveChannel) {
|
||||||
|
console.log(`Receive channel's status has changed to ${receiveChannel.readyState}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function receiveChannelCallback(event) {
|
||||||
|
let receiveChannel = event.channel;
|
||||||
|
receiveChannel.onmessage = handleReceiveMessage;
|
||||||
|
receiveChannel.onopen = handleReceiveChannelStatusChange;
|
||||||
|
receiveChannel.onclose = handleReceiveChannelStatusChange;
|
||||||
|
}
|
||||||
|
function sendMessage(message) {
|
||||||
|
sendChannel.send(message);
|
||||||
|
}
|
||||||
|
function handleReceiveMessage(event) {
|
||||||
|
console.log(event.data);
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=webRTC.js.map
|
||||||
1
webRTC.js.map
Normal file
1
webRTC.js.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"webRTC.js","sourceRoot":"","sources":["src/webRTC.ts"],"names":[],"mappings":";AAAA,MAAM,MAAM,GAAG;IACX,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,yBAAyB,EAAE,CAAC;CACpD,CAAC;AAEF,IAAI,eAAe,GAAG,IAAI,iBAAiB,EAAE,CAAC;AAE9C,SAAS,6BAA6B;IAClC,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;AAC/C,CAAC;AAED,IAAI,WAAW,GAAG,eAAe,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC;AACnE,WAAW,CAAC,MAAM,GAAG,6BAA6B,CAAC;AACnD,WAAW,CAAC,OAAO,GAAG,6BAA6B,CAAC;AAGpD,IAAI,gBAAgB,GAAG,IAAI,iBAAiB,EAAE,CAAC;AAC/C,gBAAgB,CAAC,aAAa,GAAG,sBAAsB,CAAC;AAGxD,eAAe,CAAC,cAAc,GAAG,CAAC,CAAK,EAAE,EAAE,CACvC,CAAC,CAAC,CAAC,SAAS;IACZ,gBAAgB,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;AAEjF,gBAAgB,CAAC,cAAc,GAAG,CAAC,CAAC,EAAE,EAAE,CACpC,CAAC,CAAC,CAAC,SAAS;IACZ,eAAe,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;AAIhF,SAAS,4BAA4B,CAAC,KAAS;IAC3C,OAAO,CAAC,GAAG,CAAC,8BAA8B,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAChE,CAAC;AAED,SAAS,8BAA8B;IACrC,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,+BAA+B;IACtC,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;AAEjD,CAAC;AAED,SAAS,uBAAuB;IAC9B,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;AAClD,CAAC;AAEH,eAAe;KACZ,WAAW,EAAE;KACb,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,eAAe,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;KAC3D,IAAI,CAAC,GAAG,EAAE,CACT,gBAAgB,CAAC,oBAAoB,CAAC,eAAe,CAAC,gBAA6C,CAAC,CACrG;KACA,IAAI,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,YAAY,EAAE,CAAC;KAC3C,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,gBAAgB,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;KAC9D,IAAI,CAAC,GAAG,EAAE,CACT,eAAe,CAAC,oBAAoB,CAAC,gBAAgB,CAAC,gBAA6C,CAAC,CACrG;KACA,KAAK,CAAC,4BAA4B,CAAC,CAAC;AAErC,SAAS,gCAAgC,CAAC,KAAS;IACjD,IAAI,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC;IAEnC,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO,CAAC,GAAG,CACT,2CAA2C,cAAc,CAAC,UAAU,EAAE,CACvE,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAS;IACvC,IAAI,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC;IACnC,cAAc,CAAC,SAAS,GAAG,oBAAoB,CAAC;IAChD,cAAc,CAAC,MAAM,GAAG,gCAAgC,CAAC;IACzD,cAAc,CAAC,OAAO,GAAG,gCAAgC,CAAC;AAC5D,CAAC;AAED,SAAS,WAAW,CAAC,OAAc;IACjC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAS;IACrC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
||||||
Reference in New Issue
Block a user