2021-10-13 07:39:06 +03:00
|
|
|
import { deSig } from '../index';
|
2021-02-25 06:54:00 +03:00
|
|
|
import { Enc, Path, Patp, PatpNoSig, Poke, Thread } from '../lib/types';
|
|
|
|
import { Group, GroupPolicy, GroupPolicyDiff, GroupUpdateAddMembers, GroupUpdateAddTag, GroupUpdateChangePolicy, GroupUpdateRemoveGroup, GroupUpdateRemoveMembers, GroupUpdateRemoveTag, Resource, RoleTags, Tag } from './types';
|
|
|
|
import { GroupUpdate } from './update';
|
|
|
|
|
2021-04-22 17:17:39 +03:00
|
|
|
export const GROUP_UPDATE_VERSION = 0;
|
|
|
|
|
|
|
|
export const proxyAction = <T>(data: T, version: number = GROUP_UPDATE_VERSION): Poke<T> => ({
|
2021-02-25 06:54:00 +03:00
|
|
|
app: 'group-push-hook',
|
2021-04-22 17:17:39 +03:00
|
|
|
mark: `group-update-${version}`,
|
2021-02-25 06:54:00 +03:00
|
|
|
json: data
|
|
|
|
});
|
|
|
|
|
2021-04-22 17:17:39 +03:00
|
|
|
const storeAction = <T extends GroupUpdate>(data: T, version: number = GROUP_UPDATE_VERSION): Poke<T> => ({
|
2021-02-25 06:54:00 +03:00
|
|
|
app: 'group-store',
|
2021-04-22 17:17:39 +03:00
|
|
|
mark: `group-update-${version}`,
|
2021-02-25 06:54:00 +03:00
|
|
|
json: data
|
|
|
|
});
|
|
|
|
|
|
|
|
export { storeAction as groupStoreAction };
|
|
|
|
|
|
|
|
const viewAction = <T>(data: T): Poke<T> => ({
|
|
|
|
app: 'group-view',
|
|
|
|
mark: 'group-view-action',
|
|
|
|
json: data
|
|
|
|
});
|
|
|
|
|
|
|
|
export { viewAction as groupViewAction };
|
|
|
|
|
|
|
|
export const viewThread = <T>(thread: string, action: T): Thread<T> => ({
|
|
|
|
inputMark: 'group-view-action',
|
|
|
|
outputMark: 'json',
|
|
|
|
threadName: thread,
|
|
|
|
body: action
|
|
|
|
});
|
|
|
|
|
|
|
|
export const removeMembers = (
|
|
|
|
resource: Resource,
|
|
|
|
ships: PatpNoSig[]
|
|
|
|
): Poke<GroupUpdateRemoveMembers> => proxyAction({
|
|
|
|
removeMembers: {
|
|
|
|
resource,
|
|
|
|
ships
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
export const addTag = (
|
|
|
|
resource: Resource,
|
|
|
|
tag: Tag,
|
|
|
|
ships: Patp[]
|
|
|
|
): Poke<GroupUpdateAddTag> => proxyAction({
|
|
|
|
addTag: {
|
|
|
|
resource,
|
|
|
|
tag,
|
|
|
|
ships
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
export const removeTag = (
|
|
|
|
tag: Tag,
|
|
|
|
resource: Resource,
|
|
|
|
ships: PatpNoSig[]
|
|
|
|
): Poke<GroupUpdateRemoveTag> => proxyAction({
|
|
|
|
removeTag: {
|
|
|
|
tag,
|
|
|
|
resource,
|
|
|
|
ships
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
export const addMembers = (
|
|
|
|
resource: Resource,
|
|
|
|
ships: PatpNoSig[]
|
|
|
|
): Poke<GroupUpdateAddMembers> => proxyAction({
|
|
|
|
addMembers: {
|
|
|
|
resource,
|
|
|
|
ships
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
export const removeGroup = (
|
|
|
|
resource: Resource
|
|
|
|
): Poke<GroupUpdateRemoveGroup> => storeAction({
|
|
|
|
removeGroup: {
|
|
|
|
resource
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
export const changePolicy = (
|
|
|
|
resource: Resource,
|
2021-06-09 04:42:27 +03:00
|
|
|
diff: Enc<GroupPolicyDiff>
|
|
|
|
): Poke<Enc<GroupUpdateChangePolicy>> => proxyAction({
|
2021-02-25 06:54:00 +03:00
|
|
|
changePolicy: {
|
|
|
|
resource,
|
|
|
|
diff
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
export const join = (
|
|
|
|
ship: string,
|
2021-11-18 00:25:24 +03:00
|
|
|
name: string,
|
|
|
|
app: "groups" | "graph",
|
|
|
|
autojoin: boolean,
|
|
|
|
share: boolean
|
2021-02-25 06:54:00 +03:00
|
|
|
): Poke<any> => viewAction({
|
|
|
|
join: {
|
|
|
|
resource: makeResource(ship, name),
|
2021-11-18 00:25:24 +03:00
|
|
|
ship,
|
|
|
|
shareContact: share || false,
|
|
|
|
app,
|
|
|
|
autojoin
|
2021-02-25 06:54:00 +03:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
export const createGroup = (
|
|
|
|
name: string,
|
|
|
|
policy: Enc<GroupPolicy>,
|
|
|
|
title: string,
|
|
|
|
description: string
|
|
|
|
): Thread<any> => viewThread('group-create', {
|
|
|
|
create: {
|
|
|
|
name,
|
|
|
|
policy,
|
|
|
|
title,
|
|
|
|
description
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
export const deleteGroup = (
|
|
|
|
ship: string,
|
|
|
|
name: string
|
|
|
|
): Thread<any> => viewThread('group-delete', {
|
|
|
|
remove: makeResource(ship, name)
|
|
|
|
});
|
|
|
|
|
|
|
|
export const leaveGroup = (
|
|
|
|
ship: string,
|
|
|
|
name: string
|
|
|
|
): Thread<any> => viewThread('group-leave', {
|
|
|
|
leave: makeResource(ship, name)
|
|
|
|
});
|
|
|
|
|
|
|
|
export const invite = (
|
|
|
|
ship: string,
|
|
|
|
name: string,
|
|
|
|
ships: Patp[],
|
|
|
|
description: string
|
|
|
|
): Thread<any> => viewThread('group-invite', {
|
|
|
|
invite: {
|
|
|
|
resource: makeResource(ship, name),
|
|
|
|
ships,
|
|
|
|
description
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2021-11-18 00:25:24 +03:00
|
|
|
export const abortJoin = (
|
2021-04-22 17:17:39 +03:00
|
|
|
resource: string
|
|
|
|
): Poke<any> => viewAction({
|
2021-11-18 00:25:24 +03:00
|
|
|
abort: resource
|
2021-04-22 17:17:39 +03:00
|
|
|
});
|
|
|
|
|
2021-02-25 06:54:00 +03:00
|
|
|
export const roleTags = ['janitor', 'moderator', 'admin'];
|
|
|
|
// TODO make this type better?
|
|
|
|
|
|
|
|
export const groupBunts = {
|
2021-10-12 23:09:20 +03:00
|
|
|
group: (): Group => ({ members: [], tags: { role: {} }, hidden: false, policy: groupBunts.policy() }),
|
|
|
|
policy: (): GroupPolicy => ({ open: { banned: [], banRanks: [] } })
|
2021-02-25 06:54:00 +03:00
|
|
|
};
|
|
|
|
|
2021-12-07 19:02:23 +03:00
|
|
|
export const joinError = ['no-perms', 'strange', 'abort'] as const;
|
2021-02-25 06:54:00 +03:00
|
|
|
export const joinResult = ['done', ...joinError] as const;
|
2021-11-18 00:25:24 +03:00
|
|
|
export const joinLoad = ['start', 'added', 'metadata'] as const;
|
|
|
|
export const joinProgress = [...joinLoad, ...joinResult] as const;
|
2021-02-25 06:54:00 +03:00
|
|
|
|
2021-10-12 23:09:20 +03:00
|
|
|
export function roleForShip(
|
2021-02-25 06:54:00 +03:00
|
|
|
group: Group,
|
|
|
|
ship: PatpNoSig
|
2021-10-12 23:09:20 +03:00
|
|
|
): RoleTags | undefined {
|
2021-02-25 06:54:00 +03:00
|
|
|
return roleTags.reduce((currRole, role) => {
|
|
|
|
const roleShips = group?.tags?.role?.[role];
|
2022-01-26 02:05:32 +03:00
|
|
|
return roleShips && roleShips.includes(ship) ? role : currRole;
|
2021-02-25 06:54:00 +03:00
|
|
|
}, undefined as RoleTags | undefined);
|
2021-11-09 03:44:04 +03:00
|
|
|
};
|
2021-02-25 06:54:00 +03:00
|
|
|
|
2021-10-12 23:09:20 +03:00
|
|
|
export function resourceFromPath(path: Path): Resource {
|
2021-02-25 06:54:00 +03:00
|
|
|
const [, , ship, name] = path.split('/');
|
|
|
|
return { ship, name };
|
|
|
|
}
|
|
|
|
|
2021-10-12 23:09:20 +03:00
|
|
|
export function makeResource(ship: string, name: string) {
|
2021-02-25 06:54:00 +03:00
|
|
|
return { ship, name };
|
|
|
|
}
|
|
|
|
|
|
|
|
export const isWriter = (group: Group, resource: string, ship: string) => {
|
2021-11-09 03:44:04 +03:00
|
|
|
const graph = group.tags?.graph;
|
2022-01-26 02:05:32 +03:00
|
|
|
const writers: string[] | undefined = graph && (graph[resource] as any)?.writers;
|
2021-10-12 23:09:20 +03:00
|
|
|
const admins = group?.tags?.role?.admin ?? [];
|
2021-11-09 03:44:04 +03:00
|
|
|
if (typeof writers === 'undefined') {
|
2021-02-25 06:54:00 +03:00
|
|
|
return true;
|
|
|
|
} else {
|
2022-01-26 02:05:32 +03:00
|
|
|
return writers.includes(ship) || admins.includes(ship);
|
2021-02-25 06:54:00 +03:00
|
|
|
}
|
2021-11-09 03:44:04 +03:00
|
|
|
};
|
2021-02-25 06:54:00 +03:00
|
|
|
|
2021-10-12 23:09:20 +03:00
|
|
|
export function isChannelAdmin(
|
2021-02-25 06:54:00 +03:00
|
|
|
group: Group,
|
|
|
|
resource: string,
|
|
|
|
ship: string
|
2021-10-12 23:09:20 +03:00
|
|
|
): boolean {
|
|
|
|
const role = roleForShip(group, deSig(ship));
|
2021-02-25 06:54:00 +03:00
|
|
|
|
|
|
|
return (
|
|
|
|
isHost(resource, ship) ||
|
|
|
|
role === 'admin' ||
|
|
|
|
role === 'moderator'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-10-12 23:09:20 +03:00
|
|
|
export function isHost(
|
2021-02-25 06:54:00 +03:00
|
|
|
resource: string,
|
|
|
|
ship: string
|
2021-10-12 23:09:20 +03:00
|
|
|
): boolean {
|
2021-02-25 06:54:00 +03:00
|
|
|
const [, , host] = resource.split('/');
|
|
|
|
|
|
|
|
return ship === host;
|
|
|
|
}
|