parent
43cc610f9b
commit
def2fe48e0
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="postgres@localhost" uuid="f9baa73b-e699-47b1-b121-ae82bb675ce1">
|
||||
<driver-ref>postgresql</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:postgresql://localhost:5432/postgres</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,7 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="TsLint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
||||
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.pnpm-store" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.svelte-kit" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/mariochat.iml" filepath="$PROJECT_DIR$/.idea/mariochat.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PrettierConfiguration">
|
||||
<option name="myRunOnSave" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,58 @@
|
||||
// Update with your config settings.
|
||||
|
||||
/**
|
||||
* @type { Object.<string, import("knex").Knex.Config> }
|
||||
*/
|
||||
module.exports = {
|
||||
|
||||
development: {
|
||||
client: 'pg',
|
||||
connection: {
|
||||
host: 'localhost',
|
||||
port: 5432,
|
||||
user: 'postgres',
|
||||
password: 'mariochatpgpw',
|
||||
database: 'postgres',
|
||||
},
|
||||
pool: {
|
||||
min: 2,
|
||||
max: 10,
|
||||
},
|
||||
migrations: {
|
||||
tableName: 'knex_migrations',
|
||||
},
|
||||
},
|
||||
|
||||
staging: {
|
||||
client: 'postgresql',
|
||||
connection: {
|
||||
database: 'my_db',
|
||||
user: 'username',
|
||||
password: 'password'
|
||||
},
|
||||
pool: {
|
||||
min: 2,
|
||||
max: 10
|
||||
},
|
||||
migrations: {
|
||||
tableName: 'knex_migrations'
|
||||
}
|
||||
},
|
||||
|
||||
production: {
|
||||
client: 'postgresql',
|
||||
connection: {
|
||||
database: 'my_db',
|
||||
user: 'username',
|
||||
password: 'password'
|
||||
},
|
||||
pool: {
|
||||
min: 2,
|
||||
max: 10
|
||||
},
|
||||
migrations: {
|
||||
tableName: 'knex_migrations'
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
@ -0,0 +1,22 @@
|
||||
|
||||
exports.up = async (knex) => {
|
||||
await knex.schema.createTable('user', (table) => {
|
||||
table.string('public_key').primary()
|
||||
table.datetime('last_seen').notNullable()
|
||||
table.binary('name').nullable()
|
||||
table.binary('avatar').nullable()
|
||||
})
|
||||
await knex.schema.createTable('room', (table) => {
|
||||
table.integer('id').primary()
|
||||
table.binary('name').notNullable()
|
||||
table.binary('room_key').notNullable()
|
||||
table.binary('avatar').nullable()
|
||||
})
|
||||
await knex.schema.createTable('user_to_room', (table) => {
|
||||
table.string('user_public_key').references('public_key').inTable('user').notNullable()
|
||||
table.integer('room_id').references('id').inTable('room').notNullable()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
exports.down = (knex) => knex.schema.dropTable('user')
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,19 +1,28 @@
|
||||
import makeMdns from 'multicast-dns'
|
||||
const mdns = makeMdns()
|
||||
import Knex from 'knex'
|
||||
import { Model } from 'objection'
|
||||
import { setVapidDetails } from 'web-push'
|
||||
|
||||
mdns.on('response', (response) => {
|
||||
console.log('got a response packet:', response)
|
||||
const knex = Knex({
|
||||
client: 'pg',
|
||||
connection: {
|
||||
host: process.env.DB_URL ?? 'localhost',
|
||||
port: 5432,
|
||||
user: 'postgres',
|
||||
password: 'mariochatpgpw',
|
||||
database: 'postgres',
|
||||
},
|
||||
})
|
||||
|
||||
mdns.on('query', function (query) {
|
||||
console.log('got a query packet:', query)
|
||||
})
|
||||
Model.knex(knex)
|
||||
|
||||
mdns.query({
|
||||
questions: [
|
||||
{
|
||||
name: 'brunhilde.local',
|
||||
type: 'A',
|
||||
},
|
||||
],
|
||||
})
|
||||
const vapidKeys = {
|
||||
publicKey:
|
||||
'BEovcVTe3AmMJ8fpMahQqjr2shW74zBW6Imvih274_03nJg9m4hhUIAPc2Ur0_2aryAOXCA9eEulplz2y0CLfwY',
|
||||
privateKey: 'DUT4Fn5r293-e5gTvpgiQtpU5yhjgwASulFZZclFaJg',
|
||||
}
|
||||
|
||||
setVapidDetails(
|
||||
'mailto:mario.bruestle@pm.me',
|
||||
vapidKeys.publicKey,
|
||||
vapidKeys.privateKey,
|
||||
)
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
import { Model } from 'objection'
|
||||
|
||||
class BaseModel extends Model {}
|
||||
|
||||
export class User extends BaseModel {
|
||||
static tableName = 'user'
|
||||
public_key!: string
|
||||
last_seen!: Date
|
||||
name?: Uint8Array
|
||||
avatar?: Uint8Array
|
||||
}
|
||||
|
||||
export class Room extends BaseModel {
|
||||
static tableName = 'room'
|
||||
id!: number
|
||||
room_key!: Uint8Array
|
||||
avatar?: Uint8Array
|
||||
|
||||
static relationMappings = {
|
||||
users: {
|
||||
relation: Model.ManyToManyRelation,
|
||||
modelClass: User,
|
||||
join: {
|
||||
from: 'room.id',
|
||||
through: {
|
||||
from: 'user_to_room.room_id',
|
||||
to: 'user_to_room.user_public_key',
|
||||
},
|
||||
to: 'user.public_key',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export class UserToRoom extends BaseModel {
|
||||
static tableName = 'user_to_room'
|
||||
user_public_key!: string
|
||||
room_id!: number
|
||||
}
|
||||
@ -0,0 +1,154 @@
|
||||
/// <reference types="@sveltejs/kit" />
|
||||
/// <reference no-default-lib="true"/>
|
||||
/// <reference lib="esnext" />
|
||||
/// <reference lib="webworker" />
|
||||
import { build, files, version } from '$service-worker'
|
||||
|
||||
const sw = self as unknown as ServiceWorkerGlobalScope
|
||||
|
||||
// Create a unique cache name for this deployment
|
||||
const CACHE = `cache-${version}`
|
||||
|
||||
const ASSETS = [
|
||||
...build, // the app itself
|
||||
...files, // everything in `static`
|
||||
'/',
|
||||
]
|
||||
|
||||
function installListener(event: ExtendableEvent) {
|
||||
console.log('installing service worker')
|
||||
// Create a new cache and add all files to it
|
||||
async function addFilesToCache() {
|
||||
const cache = await caches.open(CACHE)
|
||||
await cache.addAll(ASSETS)
|
||||
}
|
||||
|
||||
event.waitUntil(addFilesToCache())
|
||||
sw.skipWaiting()
|
||||
}
|
||||
|
||||
function activateListener(event: ExtendableEvent) {
|
||||
console.log('activating service worker')
|
||||
// Remove previous cached data from disk
|
||||
async function deleteOldCaches() {
|
||||
let cachesWereDeleted = false
|
||||
for (const key of await caches.keys()) {
|
||||
if (key !== CACHE) {
|
||||
await caches.delete(key)
|
||||
cachesWereDeleted = true
|
||||
}
|
||||
}
|
||||
sw.clients.claim()
|
||||
if (cachesWereDeleted) {
|
||||
sw.clients.matchAll().then((clients) => {
|
||||
clients.forEach((client) => {
|
||||
if ('navigate' in client) {
|
||||
console.log('refreshing on sw update')
|
||||
return (client as { navigate: (url: string) => void }).navigate(
|
||||
client.url,
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
event.waitUntil(deleteOldCaches())
|
||||
}
|
||||
|
||||
function fetchListener(event: FetchEvent) {
|
||||
// ignore POST requests etc
|
||||
if (event.request.method !== 'GET' || event.request.headers.has('range'))
|
||||
return
|
||||
|
||||
async function respond() {
|
||||
const url = new URL(event.request.url)
|
||||
const cache = await caches.open(CACHE)
|
||||
|
||||
// `build`/`files` can always be served from the cache
|
||||
if (ASSETS.includes(url.pathname)) {
|
||||
return (await cache.match(event.request)) ?? Response.error()
|
||||
}
|
||||
|
||||
// for everything else, try the network first, but
|
||||
// fall back to the cache if we're offline
|
||||
try {
|
||||
const response = await fetch(event.request)
|
||||
|
||||
if (response.status === 200) {
|
||||
cache.put(event.request, response.clone())
|
||||
}
|
||||
return response
|
||||
} catch {
|
||||
// prerendering and dynamic routes don't work all that well together
|
||||
// so just redirect to MAD start page if we're offline
|
||||
if (url.pathname !== '/') {
|
||||
return Response.redirect('/', 302)
|
||||
}
|
||||
return (await cache.match(event.request)) || Response.error()
|
||||
}
|
||||
}
|
||||
|
||||
event.respondWith(respond())
|
||||
}
|
||||
|
||||
function backgroundFetchListener(event: BackgroundFetchEvent) {
|
||||
const bgFetch = event.registration
|
||||
event.waitUntil(
|
||||
(async () => {
|
||||
try {
|
||||
console.log('backgorund fetch called!')
|
||||
} catch (error) {
|
||||
await bgFetch.abort()
|
||||
console.error('Background fetch failed:', error)
|
||||
}
|
||||
})(),
|
||||
)
|
||||
}
|
||||
|
||||
sw.addEventListener('install', installListener)
|
||||
sw.addEventListener('activate', activateListener)
|
||||
sw.addEventListener('fetch', fetchListener)
|
||||
sw.addEventListener('backgroundfetch', backgroundFetchListener)
|
||||
|
||||
async function requestNotificationPermission() {
|
||||
const permission = await Notification.requestPermission()
|
||||
if (permission !== 'granted') {
|
||||
console.error('Notification permission not granted')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
requestNotificationPermission()
|
||||
|
||||
import { initializeApp } from 'firebase/app'
|
||||
import firebase from 'firebase/compat'
|
||||
|
||||
// Your web app's Firebase configuration
|
||||
const firebaseConfig = {
|
||||
apiKey: 'AIzaSyBjO3BntXN4pi_Is8qB-AgFrTsssP8u0Hc',
|
||||
authDomain: 'mariochat-42e86.firebaseapp.com',
|
||||
projectId: 'mariochat-42e86',
|
||||
storageBucket: 'mariochat-42e86.appspot.com',
|
||||
messagingSenderId: '650934549094',
|
||||
appId: '1:650934549094:web:48a55d718de5b3c7a64ba7',
|
||||
}
|
||||
const app = initializeApp(firebaseConfig)
|
||||
const messaging = firebase.messaging()
|
||||
|
||||
async function requestNotificationPermissionAndToken(): Promise<string | null> {
|
||||
const permissionGranted = await requestNotificationPermission()
|
||||
if (!permissionGranted) return null
|
||||
|
||||
try {
|
||||
const token = await messaging.getToken({
|
||||
vapidKey:
|
||||
'BEovcVTe3AmMJ8fpMahQqjr2shW74zBW6Imvih274_03nJg9m4hhUIAPc2Ur0_2aryAOXCA9eEulplz2y0CLfwY',
|
||||
})
|
||||
return token
|
||||
} catch (error) {
|
||||
console.error('Failed to get the registration token:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue