better inviatations + clean up

This commit is contained in:
jusax23 2023-03-28 19:23:03 +02:00
parent ee10e75464
commit 20b72e9798
Signed by: jusax23
GPG key ID: 499E2AA870C1CD41
13 changed files with 203 additions and 161 deletions

View file

@ -1,5 +1,5 @@
# .woodpecker.yml
platform: linux/arm64
#platform: linux/arm64
pipeline:
build:

View file

@ -1,5 +1,5 @@
# .woodpecker.yml
platform: linux/arm64
#platform: linux/arm64
pipeline:
build:

8
package-lock.json generated
View file

@ -12,7 +12,7 @@
"auth-header": "^1.0.0",
"commander": "^10.0.0",
"cors": "^2.8.5",
"dblang": "https://jusax.de/git/attachments/377d0a32-3eca-4a8f-9c3a-f8a045a9c5b1",
"dblang": "https://jusax.de/git/attachments/84353ff6-f81e-450b-93e1-d0a4d6d4556f",
"express": "^4.18.2",
"juml": "https://jusax.de/git/attachments/208913c5-2851-4b86-a53d-ca99fed168cc",
"nman": "https://jusax.de/git/attachments/5333948b-fe6b-45d2-9230-ca388f6a89bc",
@ -1107,9 +1107,9 @@
}
},
"node_modules/dblang": {
"version": "0.9.3",
"resolved": "https://jusax.de/git/attachments/377d0a32-3eca-4a8f-9c3a-f8a045a9c5b1",
"integrity": "sha512-Co3RZ2Dfk2Atm2Oyr7rtHJDeiMZ8NwfrvTBwfhP9wVkXQuN1WrMMQ5W+/Ho2g6c6BWbUnsqADKTaCcJZrYBbjQ==",
"version": "0.9.5",
"resolved": "https://jusax.de/git/attachments/84353ff6-f81e-450b-93e1-d0a4d6d4556f",
"integrity": "sha512-g7hBlnib2Tg7DoeGaygvnzBCPn1m47yrS9rMHkThqRfQp43wT/gUWqmnvxunNMVo0ka7PEwdwW4FpKz7VnpGqA==",
"license": "UNLICENSED",
"dependencies": {
"gitea-release": "git+https://jusax.de/git/jusax23/gitea-release.git",

View file

@ -57,7 +57,7 @@
"auth-header": "^1.0.0",
"commander": "^10.0.0",
"cors": "^2.8.5",
"dblang": "https://jusax.de/git/attachments/377d0a32-3eca-4a8f-9c3a-f8a045a9c5b1",
"dblang": "https://jusax.de/git/attachments/84353ff6-f81e-450b-93e1-d0a4d6d4556f",
"express": "^4.18.2",
"juml": "https://jusax.de/git/attachments/208913c5-2851-4b86-a53d-ca99fed168cc",
"nman": "https://jusax.de/git/attachments/5333948b-fe6b-45d2-9230-ca388f6a89bc",

View file

@ -2,3 +2,4 @@ export * from "./acts/login.js";
export * from "./acts/client.js"
export * from "./acts/admin.js"
export * from "./acts/rooms.js"
export * from "./acts/server.js"

View file

@ -25,8 +25,8 @@ export const getAccounts: Act = {
let accID = d[accounts.accID];
let rights = d[accounts.rights];
let name = d[accounts.name];
let viewable = d[accounts.viewable];
let deleted = d[accounts.deleted];
let viewable = d[accounts.viewable] ? true : false;
let deleted = d[accounts.deleted] ? true : false;
let maxRooms = d[accounts.maxRooms];
let maxRoomSize = d[accounts.maxRoomSize];
let maxUsersPerRoom = d[accounts.maxUsersPerRoom];

View file

@ -1,7 +1,7 @@
import { and, eq, ge, insert, leq, remove, select, update } from "dblang";
import { PERMISSIONS } from "../../server/permissions.js";
import { sha256, sign } from "../../sys/crypto.js";
import { accounts, db, invitations, roomMembers, rooms } from "../../sys/db.js";
import { accounts, db, roomMembers, rooms } from "../../sys/db.js";
import { selfTag } from "../../sys/selfTag.js";
import { getSettings, SETTINGS } from "../../sys/settings.js";
import { get64, uts } from "../../sys/tools.js";
@ -55,7 +55,7 @@ export const getMyAccount: Act = {
if (query.length > 0) {
let rights = query[0][accounts.rights];
let name = query[0][accounts.name];
let viewable = query[0][accounts.viewable];
let viewable = query[0][accounts.viewable] ? true : false;
let maxRooms = query[0][accounts.maxRooms];
let maxRoomSize = query[0][accounts.maxRoomSize];
let maxUsersPerRoom = query[0][accounts.maxUsersPerRoom];
@ -141,8 +141,8 @@ export const createRoom: Act = {
data.icon
).query(db);
if (req.affectedRows > 0) {
await insert(roomMembers.roomID, roomMembers.name, roomMembers.server, roomMembers.admin)
.add(req.insertId, client.name, "local", true)
await insert(roomMembers.roomID, roomMembers.name, roomMembers.server, roomMembers.admin, roomMembers.confirmed)
.add(req.insertId, client.name, "local", true, true)
.query(db);
aws("ok", "");
} else {
@ -203,56 +203,3 @@ export const listPublicRooms: Act = {
aws("ok", out.filter(d => d != null));
}
};
export const getInvitations: Act = {
state: STATE.client,
right: PERMISSIONS.CAN_USE_API,
data: {},
func: async (client: Client, data: any, aws: (code: string, data: any) => void) => {
await remove(invitations)
.where(leq(invitations.expires, uts()))
.query(db);
let req = await select([
invitations.invitationID,
invitations.room,
invitations.server,
invitations.ota,
invitations.expires,
invitations.fromName,
invitations.fromServer,
], invitations)
.where(eq(invitations.accID, client.accID))
.query(db);
let out = req.map(d => {
let invitationID = d[invitations.invitationID];
let room = d[invitations.room];
let server = d[invitations.server];
let ota = d[invitations.ota];
let expires = d[invitations.expires];
let fromName = d[invitations.fromName];
let fromServer = d[invitations.fromServer];
if (invitationID != null && room != null && server != null && ota != null && expires != null && fromName != null && fromServer != null) {
return { invitationID, room, server, ota, expires, fromName, fromServer };
}
return null;
});
aws("ok", out.filter(d => d != null));
}
};
export const deleteInvitation: Act = {
state: STATE.client,
right: PERMISSIONS.CAN_USE_API,
data: {
invitationID: "number",
},
func: async (client: Client, data: any, aws: (code: string, data: any) => void) => {
let req = await remove(invitations)
.where(and(
eq(invitations.accID, client.accID),
eq(invitations.invitationID, data.invitationID),
)).query(db);
if (req.affectedRows > 0) aws("ok", "");
else aws("error", "existence");
}
};

View file

@ -1,10 +1,11 @@
import { alias, and, eq, exists, geq, innerJoinOn, innerJoinUsing, insert, le, minus, naturalJoin, not, or, remove, select, update } from "dblang";
import { checkSelfTag, outbagURLfromTag } from "../../server/outbagURL.js";
import { ROOM_RIGHTS } from "../../server/permissions.js";
import { accounts, db, invitations, remoteRooms, roomMembers, roomOTAs, rooms } from "../../sys/db.js";
import { accounts, db, remoteRooms, roomMembers, roomOTAs, rooms } from "../../sys/db.js";
import { selfTag } from "../../sys/selfTag.js";
import { uts } from "../../sys/tools.js";
import { isRoomFull } from "../helper.js";
import { fetchRemoteAsServer } from "../server.js";
import { Act, Client, STATE } from "../user.js";
export const listRooms: Act = {
@ -23,7 +24,8 @@ export const listRooms: Act = {
rooms.visibility,
rooms.title,
rooms.description,
rooms.icon
rooms.icon,
roomMembers.confirmed
], innerJoinUsing(rooms, roomMembers, rooms.roomID, roomMembers.roomID))
.where(and(
eq(roomMembers.name, client.name),
@ -38,29 +40,73 @@ export const listRooms: Act = {
let title = d[rooms.title];
let description = d[rooms.description];
let icon = d[rooms.icon];
let confirmed = d[roomMembers.confirmed] ? true : false;
let server = selfTag.tag;
if (name != null && owner != null && rights != null && visibility != null && title != null && description != null && icon != null) {
return { name, server, owner, rights, visibility, title, description, icon, debug: global.debug };
if (name != null && owner != null && rights != null && visibility != null && title != null && description != null && icon != null && confirmed != null) {
return { name, server, owner, rights, visibility, title, description, icon, debug: global.debug, confirmed };
}
console.log(name, server, owner, rights, visibility, title, description, icon, global.debug, confirmed)
return null;
})
});
if (client.state == STATE.client) {
let query = await select([remoteRooms.server], remoteRooms)
let query = await select([
remoteRooms.server,
remoteRooms.room,
remoteRooms.confirmed
], remoteRooms)
.where(eq(remoteRooms.accID, client.accID))
.query(db);
for (let i = 0; i < query.length; i++) {
const server = query[i];
let resp = await client.pass(server[remoteRooms.server], "listRooms", {});
let serverList = [...new Set(query.map(t => t[remoteRooms.server]))];
let toAddRooms: ([number, string, string])[] = [];
for (let i = 0; i < serverList.length; i++) {
const server = serverList[i];
let serverRooms = Object.fromEntries(query
.filter(d => d[remoteRooms.server] == server)
.map(d => [d[remoteRooms.room] + "@" + d[remoteRooms.server], {
server: d[remoteRooms.server],
room: d[remoteRooms.room],
confirmed: d[remoteRooms.confirmed] ? true : false
}])
);
let resp = await client.pass(server, "listRooms", {});
if (resp.state == "ok" && Array.isArray(resp.data)) {
out.push(...resp.data.map(d => {
let { name, owner, rights, visibility, title, description, icon, server, debug } = d;
for (let j = 0; j < resp.data.length; j++) {
const rRooms = resp.data[j];
try {
let { name, owner, rights, visibility, title, description, icon, debug } = rRooms;
if (name != null && owner != null && rights != null && visibility != null && title != null && description != null && icon != null && debug != null) {
return { name, server, owner, rights, visibility, title, description, icon, debug };
let sRoom = serverRooms[rRooms.room + "@" + rRooms.server];
if (sRoom == null) toAddRooms.push([client.accID, name, server]);
out.push({
name, owner, rights, visibility, title, description, icon, server, debug, confirmed: sRoom?.confirmed ?? false
});
delete serverRooms[rRooms.room + "@" + rRooms.server];
}
return null;
}))
} catch (error) { }
}
for (const k in serverRooms) {
const unfoundRoom = serverRooms[k];
await remove(remoteRooms)
.where(and(
eq(remoteRooms.accID, client.accID),
eq(remoteRooms.server, unfoundRoom.server),
eq(remoteRooms.room, unfoundRoom.room)
)).query(db);
}
} else {
//may add unfound rooms
}
}
try {
await insert(remoteRooms.accID, remoteRooms.room, remoteRooms.server)
.addValues(...toAddRooms)
.query(db);
} catch (error) { }
}
aws("ok", out.filter(d => d != null));
}
@ -141,17 +187,19 @@ export const getRoomMembers: Act = {
let req = await select([
roomMembers.name,
roomMembers.server,
roomMembers.admin
roomMembers.admin,
roomMembers.confirmed,
], roomMembers)
.where(eq(roomMembers.roomID, roomID))
.query(db);
let out = req.map(d => {
let name = d[roomMembers.name];
let server = d[roomMembers.server];
let admin = d[roomMembers.admin];
let admin = d[roomMembers.admin] ? true : false;
let confirmed = d[roomMembers.confirmed] ? true : false;
server = server == "local" ? selfTag.tag : server;
if (name != null && server != null && admin != null) {
return { name, server, admin };
if (name != null && server != null && admin != null && confirmed != null) {
return { name, server, admin, confirmed };
}
return null;
});
@ -173,8 +221,8 @@ export const joinRoom: Act = {
let resp = await client.pass(data.server, "joinRoom", data);
if (resp.state == "ok") {
try {
await insert(remoteRooms.accID, remoteRooms.server)
.add(client.accID, data.server)
await insert(remoteRooms.accID, remoteRooms.server, remoteRooms.room, remoteRooms.confirmed)
.add(client.accID, data.server, data.room, true)
.query(db);
} catch (error) { }
} else if (resp.data == "ota") {
@ -248,8 +296,8 @@ export const joinPublicRoom: Act = {
let resp = await client.pass(data.server, "joinPublicRoom", data);
if (resp.state == "ok") {
try {
await insert(remoteRooms.accID, remoteRooms.server)
.add(client.accID, data.server)
await insert(remoteRooms.accID, remoteRooms.server, remoteRooms.room, remoteRooms.confirmed)
.add(client.accID, data.server, data.room, true)
.query(db);
} catch (error) { }
}
@ -303,7 +351,7 @@ export const getRoomOTAs: Act = {
}
let roomID = await client.isRoomAdmin(data.room, ROOM_RIGHTS.OTA);
if (roomID == -1) return void aws("error", "roomAdmin");
let req = await select([roomOTAs.token, roomOTAs.name, roomOTAs.expires, roomOTAs.usesLeft, roomOTAs.isInvitation], roomOTAs)
let req = await select([roomOTAs.token, roomOTAs.name, roomOTAs.expires, roomOTAs.usesLeft], roomOTAs)
.where(eq(roomOTAs.roomID, roomID))
.query(db);
aws("ok", req.map(d => ({
@ -311,7 +359,6 @@ export const getRoomOTAs: Act = {
name: d[roomOTAs.name],
expires: d[roomOTAs.expires],
usesLeft: d[roomOTAs.usesLeft],
isInvitation: d[roomOTAs.isInvitation],
})));
}
};
@ -326,7 +373,6 @@ export const addRoomOTA: Act = { // or change it, primary key is room and token
name: "string-256",
expires: "number",
usesLeft: "number",
isInvitation: "boolean"
},
func: async (client: Client, data: any, aws: (code: string, data: any) => void) => {
if (!checkSelfTag(data.server)) {
@ -338,15 +384,14 @@ export const addRoomOTA: Act = { // or change it, primary key is room and token
let roomID = await client.isRoomAdmin(data.room, ROOM_RIGHTS.OTA);
if (roomID == -1) return void aws("error", "roomAdmin");
try {
await insert(roomOTAs.roomID, roomOTAs.token, roomOTAs.name, roomOTAs.expires, roomOTAs.usesLeft, roomOTAs.isInvitation)
.add(roomID, data.token, data.name, data.expires, data.usesLeft, data.isInvitation)
await insert(roomOTAs.roomID, roomOTAs.token, roomOTAs.name, roomOTAs.expires, roomOTAs.usesLeft)
.add(roomID, data.token, data.name, data.expires, data.usesLeft)
.query(db);
} catch (error) {
await update(roomOTAs)
.set(roomOTAs.expires, data.expires)
.set(roomOTAs.usesLeft, data.usesLeft)
.set(roomOTAs.name, data.name)
.set(roomOTAs.isInvitation, data.isInvitation)
.where(and(
eq(roomOTAs.token, data.token),
eq(roomOTAs.roomID, roomID)
@ -382,57 +427,46 @@ export const deleteRoomOTA: Act = {
}
};
export const invite: Act = {
state: STATE.client,
export const inviteUser: Act = {
state: STATE.client | STATE.remote,
right: 0,
data: {
room: "name-100",
roomServer: "string",
name: "string",
server: "string",
token: "string-256", // ota
expires: "number", // max 10 Days
},
func: async (client: Client, data: any, aws: (code: string, data: any) => void) => {
if (!checkSelfTag(data.server)) {
if (!checkSelfTag(data.roomServer)) {
if (client.state != STATE.client) return void aws("error", "right");
let resp = await client.pass(data.server, "invite", data);
let resp = await client.pass(data.roomServer, "deleteRoomOTA", data);
aws(resp.state, resp.data);
return;
}
try {
let roomServer = await outbagURLfromTag(data.roomServer);
let resp = await select([accounts.accID], accounts)
.where(eq(accounts.name, data.name))
.query(db);
if (resp.length == 0) {
let roomID = await client.isRoomAdmin(data.room, ROOM_RIGHTS.OTA);
if (roomID == -1) return void aws("error", "roomAdmin");
let userServer = data.server;
if (!checkSelfTag(userServer)) {
let resp = await fetchRemoteAsServer(userServer, "invite", data);
if (resp.state == "error") {
client.suspect();
client.suspect();
return void aws("error", "existence");
}
if (data.expires <= uts() + 1) return void aws("ok", "");
await insert(
invitations.accID,
invitations.room,
invitations.server,
invitations.ota,
invitations.expires,
invitations.fromName,
invitations.fromServer
).add(
resp[0].accID,
data.room,
roomServer.tag,
data.token,
Math.min(data.expires, uts() + 60 * 60 * 24 * 10),
client.name,
client.server.tag,
).query(db);
} else {
userServer = "local";
}
// on roomServer
let req = await insert(roomMembers.roomID, roomMembers.server, roomMembers.name, roomMembers.admin)
.add(roomID, userServer, data.name, false)
.query(db);
if (req.affectedRows > 0) aws("ok", "");
else aws("error", "existence");
}
};
} catch (error) {
return void aws("error", "existence");
}
}
}
export const kickMember: Act = {
state: STATE.client | STATE.remote,
@ -525,7 +559,8 @@ export const leaveRoom: Act = {
await remove(remoteRooms)
.where(and(
eq(client.accID, remoteRooms.accID),
eq(data.server, remoteRooms.server)
eq(data.server, remoteRooms.server),
eq(data.room, remoteRooms.room),
)).query(db);
}
aws(resp.state, resp.data);
@ -572,6 +607,7 @@ export const setVisibility: Act = {
aws(resp.state, resp.data);
return;
}
if (!([0, 1, 2]).includes(data.visibility)) return void aws("error", "data");
let roomID = await client.isRoomAdmin(data.room, ROOM_RIGHTS.OTA);
if (roomID == -1) return void aws("error", "roomAdmin");
let req = await update(rooms)

31
src/api/acts/server.ts Normal file
View file

@ -0,0 +1,31 @@
import { eq, insert, select } from "dblang";
import { checkSelfTag } from "../../server/outbagURL.js";
import { accounts, db, remoteRooms } from "../../sys/db.js";
import { Act, Client, STATE } from "../user.js";
export const invite: Act = {
state: STATE.server,
right: 0,
data: {
room: "name-100",
//roomServer: "string",
name: "string",
server: "string",
},
func: async (client: Client, data: any, aws: (code: string, data: any) => void) => {
if (!checkSelfTag(data.server)) return void aws("error", "existence");
let req = await select([accounts.accID], accounts)
.where(eq(accounts.name, data.name))
.query(db);
if (req.length == 0) {
client.suspect();
aws("error", "existence");
return;
}
let query = await insert(remoteRooms.accID, remoteRooms.server, remoteRooms.rooms, remoteRooms.confirmed)
.add(req[0][accounts.accID], client.server.tag, data.room, false)
.query(db);
if (query.affectedRows > 0) aws("ok", "");
else aws("error", "existence");
}
};

View file

@ -80,7 +80,39 @@ export const fetchRemoteAs = async (server: outbagServer, name: string, act: str
data: "remote"
}
}
};
export const fetchRemoteAsServer = async (server: outbagServer, act: string, data: any) => {
try {
let token = await getServerToken(server);
if (token === false) throw new Error("remote");
let resp = await sendPost(
server,
{ "authorization": `Bearer ${token}` },
act,
data
);
if (resp.state != "error" || resp.data != "token") return resp;
token = await getServerToken(server, true);
if (token === false) throw new Error("remote");
resp = await sendPost(
server,
{ "authorization": `Bearer ${token}` },
act,
data
);
if (resp.state == "error" && resp.data == "token") return {
state: "error",
data: "remote"
}
return resp;
} catch (error) {
return {
state: "error",
data: "remote"
}
}
};
let cancleClear = setInterval(() => {
let keys = Object.keys(remoteTempTokens);

View file

@ -78,26 +78,15 @@ remoteRooms.addAttributes({
server: {
type: VARCHAR(256),
primaryKey: true,
}
});
export const invitations = db.newTable("invitations");
invitations.addAttributes({
invitationID: { type: INT, primaryKey: true, autoIncrement: true },
accID: {
type: INT,
foreignKey: {
link: accounts.accID,
onDelete: onAction.cascade,
onUpdate: onAction.cascade
}
},
room: { type: VARCHAR(256) },
server: { type: VARCHAR(256) },
ota: { type: VARCHAR(128) },
expires: { type: BIGINT },
fromName: { type: VARCHAR(256) },
fromServer: { type: VARCHAR(256) },
room: {
type: VARCHAR(256),
primaryKey: true,
},
confirmed: {
type: BOOL,
default: false
}
});
export const settings = db.newTable("settings");
@ -132,7 +121,7 @@ rooms.addAttributes({
}
},
rights: { type: INT, default: 0b11111 },
visibility: { type: BOOL, default: 0 },
visibility: { type: SMALLINT, default: 0 },
title: { type: TEXT, default: "" },
description: { type: TEXT, default: "" },
icon: { type: TEXT, default: "" }
@ -151,7 +140,8 @@ roomMembers.addAttributes({
},
name: { type: VARCHAR(256) },
server: { type: VARCHAR(256) },
admin: { type: BOOL, default: false }
admin: { type: BOOL, default: false },
confirmed: { type: BOOL, default: false }
});
roomMembers.addConstraint(uniqueKey([
roomMembers.roomID,
@ -177,7 +167,6 @@ roomOTAs.addAttributes({
},
expires: { type: BIGINT },
usesLeft: { type: INT },
isInvitation: { type: BOOL },
});
export const listCategories = db.newTable("listCategories");

View file

@ -193,7 +193,8 @@ const list = [
description: "some desc",
visibility: 0,
icon: "shopping",
debug: true
debug: true,
confirmed: true
}, {
name: room2,
server: "localhost:7224",
@ -203,7 +204,8 @@ const list = [
description: "some desc 2",
visibility: 1,
icon: "",
debug: true
debug: true,
confirmed: true
}
]);
await req({
@ -239,7 +241,8 @@ const list = [
description: "some desc",
visibility: 0,
icon: "shopping",
debug: true
debug: true,
confirmed: true
}
]);
}]

View file

@ -187,7 +187,8 @@ const list = [
description: "some desc",
visibility: 0,
icon: "shopping",
debug: true
debug: true,
confirmed: true
}, {
name: room2,
server: "localhost:7224",
@ -197,7 +198,8 @@ const list = [
description: "some desc 2",
visibility: 1,
icon: "",
debug: true
debug: true,
confirmed: true
}
]);
await req(handler, "deleteRoom", {
@ -222,7 +224,8 @@ const list = [
description: "some desc",
visibility: 0,
icon: "shopping",
debug: true
debug: true,
confirmed: true
}
]);