Compare commits
2 Commits
9d12e81e07
...
5b6995d6a6
Author | SHA1 | Date | |
---|---|---|---|
|
5b6995d6a6 | ||
|
65db4d8b7e |
26
back/abl/Account-abl.ts
Normal file
26
back/abl/Account-abl.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { Response } from "express";
|
||||
import { getAccounts, registerAccount } from "../db/api";
|
||||
import { getAddresseId } from "../db/utils/mail";
|
||||
import statusCodes from "../utils/statusCodes";
|
||||
|
||||
export default class Account {
|
||||
static async getAll(body, res: Response) {
|
||||
getAccounts().then((data) => {
|
||||
res.status(statusCodes.OK).json(data);
|
||||
});
|
||||
}
|
||||
|
||||
static async register(body, res: Response) {
|
||||
const { email, pwd, xoauth, xoauth2, host, port, tls } = body;
|
||||
getAddresseId(email).then((addressId) => {
|
||||
registerAccount(addressId, pwd, xoauth, xoauth2, host, port, tls)
|
||||
.then((mailboxId) => {
|
||||
res.status(statusCodes.OK).json({ id: mailboxId });
|
||||
})
|
||||
.catch(() => {
|
||||
res.status(statusCodes.INTERNAL_SERVER_ERROR);
|
||||
});
|
||||
});
|
||||
// todo change mailbox to account
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
import statusCode from "../utils/statusCodes";
|
||||
import { getRooms } from "../db/api";
|
||||
import logger from "../system/Logger";
|
||||
import { Response } from "express";
|
||||
|
||||
export async function rooms(body, res) {
|
||||
export async function rooms(body, res: Response) {
|
||||
const { mailboxId, offset, limit } = body;
|
||||
getRooms(mailboxId).then((rooms) => {
|
||||
res.status(statusCode.OK).json(rooms);
|
@ -1,18 +0,0 @@
|
||||
import statusCode from "../utils/statusCodes";
|
||||
import { registerAccount } from "../db/api";
|
||||
import { getAddresseId } from "../db/utils/mail";
|
||||
|
||||
export async function addAccount(body, res) {
|
||||
const { email, pwd, xoauth, xoauth2, host, port, tls } = body;
|
||||
getAddresseId(email).then((addressId) => {
|
||||
registerAccount(addressId, pwd, xoauth, xoauth2, host, port, tls)
|
||||
.then((mailboxId) => {
|
||||
res.status(statusCode.OK).json({ id: mailboxId });
|
||||
})
|
||||
.catch(() => {
|
||||
res.status(statusCode.INTERNAL_SERVER_ERROR);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// todo change mailbox to account
|
@ -52,7 +52,7 @@ export async function getRooms(mailboxId) {
|
||||
return await execQueryAsync(query, values);
|
||||
}
|
||||
|
||||
export async function getMessages(roomId) {
|
||||
export async function getMessages(roomId: number) {
|
||||
// todo attachements name
|
||||
const query = `
|
||||
SELECT
|
||||
@ -62,7 +62,8 @@ export async function getMessages(roomId) {
|
||||
GROUP_CONCAT(ccT.address_id) AS ccA,
|
||||
subjectT.value AS subject,
|
||||
content.text AS content,
|
||||
message.idate AS date
|
||||
message.idate AS date,
|
||||
GROUP_CONCAT(flagT.flag_name) AS flags
|
||||
FROM app_room_message msg
|
||||
|
||||
${queryFromId} fromT ON msg.message_id = fromT.message_id
|
||||
@ -88,6 +89,9 @@ export async function getMessages(roomId) {
|
||||
bodypart.bodypart_id = header_field.bodypart_id
|
||||
) content ON msg.message_id = content.message_id
|
||||
|
||||
LEFT JOIN flag ON flag.message_id = msg.message_id
|
||||
LEFT JOIN flag_name flagT ON flagT.flag_id = flag.flag_id
|
||||
|
||||
INNER JOIN message ON message.message_id = msg.message_id
|
||||
|
||||
WHERE msg.room_id = ?
|
||||
|
@ -36,7 +36,6 @@ export async function createRoom(
|
||||
return await execQueryAsyncWithId(query, values);
|
||||
}
|
||||
|
||||
// todo date not good
|
||||
export async function registerMessageInRoom(messageId: number, roomId: number, idate: string | undefined | null) {
|
||||
if (!idate) idate = new Date().toString();
|
||||
const query = `INSERT IGNORE INTO app_room_message (message_id, room_id) VALUES (?, ?)`;
|
||||
|
@ -164,7 +164,7 @@ CREATE TABLE app_room_member (
|
||||
|
||||
-- 15
|
||||
create table flag_name (
|
||||
flag_id INT NOT NULL,
|
||||
flag_id INT AUTO_INCREMENT,
|
||||
flag_name VARCHAR(255) NOT NULL,
|
||||
PRIMARY KEY (flag_id),
|
||||
UNIQUE KEY (flag_name)
|
||||
|
@ -2,9 +2,8 @@ const queryAddress = (type: string): string => `
|
||||
LEFT JOIN (
|
||||
SELECT address_field.address_id, address_field.message_id
|
||||
FROM address_field
|
||||
INNER JOIN field_name
|
||||
INNER JOIN field_name ON field_name.field_id = address_field.field_id
|
||||
WHERE
|
||||
field_name.field_id = address_field.field_id AND
|
||||
field_name.field_name = '${type}'
|
||||
)
|
||||
`;
|
||||
|
@ -77,6 +77,7 @@ export default class RegisterMessageInApp {
|
||||
|
||||
async incrementNotSeen(roomId: number) {
|
||||
// todo it appears there is an error with notifications
|
||||
console.log("incrementRead", roomId)
|
||||
if (!this.isSeen) {
|
||||
await incrementNotSeenRoom(roomId);
|
||||
}
|
||||
|
@ -2,53 +2,77 @@ import statusCodes from "../utils/statusCodes";
|
||||
import express from "express";
|
||||
const router = express.Router();
|
||||
|
||||
import Ajv from "ajv";
|
||||
import addFormats from "ajv-formats";
|
||||
const ajv = new Ajv({ allErrors: true });
|
||||
addFormats(ajv);
|
||||
import schema_account from "../schemas/account_schema.json";
|
||||
import { addAccount } from "../controllers/addAccount";
|
||||
import { getAccounts } from "../db/api";
|
||||
import { rooms } from "../controllers/rooms";
|
||||
import { messages } from "../controllers/messages";
|
||||
import { members } from "../controllers/members";
|
||||
|
||||
const validate_account = ajv.compile(schema_account);
|
||||
import { rooms } from "../abl/rooms";
|
||||
import { messages } from "../abl/messages";
|
||||
import { members } from "../abl/members";
|
||||
import {
|
||||
validateCreateAccount,
|
||||
validateGetAccounts,
|
||||
validateGetMembers,
|
||||
validateGetMessages,
|
||||
validateGetRooms,
|
||||
} from "../validator/validator";
|
||||
import Account from "../abl/Account-abl";
|
||||
|
||||
/**
|
||||
* Return all mailboxes and folders for an user
|
||||
*/
|
||||
router.get("/accounts", (req, res) => {
|
||||
getAccounts().then((data) => {
|
||||
res.status(statusCodes.OK).json(data);
|
||||
});
|
||||
router.get("/accounts", async (req, res) => {
|
||||
const valid = validateGetAccounts(req.params);
|
||||
if (!valid) {
|
||||
res.status(statusCodes.NOT_ACCEPTABLE).send({ error: validateGetAccounts.errors });
|
||||
} else {
|
||||
await Account.getAll(req.params, res);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Return all rooms from a mailbox
|
||||
*/
|
||||
router.get("/:mailboxId/rooms", async (req, res) => {
|
||||
// todo use offset limit
|
||||
await rooms(req.params, res);
|
||||
// todo offet limit
|
||||
const valid = validateGetRooms(req.params);
|
||||
if (!valid) {
|
||||
res.status(statusCodes.NOT_ACCEPTABLE).send({ error: validateGetRooms.errors });
|
||||
} else {
|
||||
await rooms(req.params, res);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Return all messages from a room
|
||||
*/
|
||||
router.get("/:roomId/messages", async (req, res) => {
|
||||
const { roomId } = req.params;
|
||||
await messages(req.params, res);
|
||||
const valid = validateGetMessages(req.params);
|
||||
if (!valid) {
|
||||
res.status(statusCodes.NOT_ACCEPTABLE).send({ error: validateGetMessages.errors });
|
||||
} else {
|
||||
await messages(req.params, res);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Return all members from a room
|
||||
*/
|
||||
router.get("/:roomId/members", async (req, res) => {
|
||||
const { roomId } = req.params;
|
||||
await members(req.params, res);
|
||||
const valid = validateGetMembers(req.params);
|
||||
if (!valid) {
|
||||
res.status(statusCodes.NOT_ACCEPTABLE).send({ error: validateGetMembers.errors });
|
||||
} else {
|
||||
await members(req.params, res);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Register a new mailbox inside the app
|
||||
*/
|
||||
router.post("/account", async (req, res) => {
|
||||
const valid = validate_account(req.body);
|
||||
const valid = validateCreateAccount(req.body);
|
||||
if (!valid) {
|
||||
res.status(statusCodes.NOT_ACCEPTABLE).send({ error: validate_account.errors });
|
||||
res.status(statusCodes.NOT_ACCEPTABLE).send({ error: validateCreateAccount.errors });
|
||||
} else {
|
||||
await addAccount(req.body, res);
|
||||
await Account.register(req.body, res);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
export default router;
|
||||
|
@ -14,6 +14,7 @@ app.use(cors());
|
||||
app.listen(process.env.PORT || 5500);
|
||||
|
||||
import mailRouter from "./routes/mail";
|
||||
import logger from "./system/Logger";
|
||||
app.use("/api/mail", mailRouter);
|
||||
|
||||
const imapSync = new ImapSync();
|
||||
@ -33,4 +34,4 @@ if (shouldReset) {
|
||||
// execQuery("DROP TABLE " + table.table_name);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
6
back/validator/schemas/getAccounts-schema.json
Normal file
6
back/validator/schemas/getAccounts-schema.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"required": [],
|
||||
"additionalProperties": false
|
||||
}
|
12
back/validator/schemas/getMembers-schema.json
Normal file
12
back/validator/schemas/getMembers-schema.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"roomId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"roomId"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
12
back/validator/schemas/getMessages-schema.json
Normal file
12
back/validator/schemas/getMessages-schema.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"roomId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"roomId"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
12
back/validator/schemas/getRooms-schema.json
Normal file
12
back/validator/schemas/getRooms-schema.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mailboxId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"mailboxId"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
16
back/validator/validator.ts
Normal file
16
back/validator/validator.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import Ajv from "ajv";
|
||||
import addFormats from "ajv-formats";
|
||||
const ajv = new Ajv({ allErrors: true });
|
||||
addFormats(ajv);
|
||||
|
||||
import createAccountSchema from "./schemas/createAccount-schema.json";
|
||||
import getAccountSchema from "./schemas/getAccounts-schema.json";
|
||||
import getRoomSchema from "./schemas/getRooms-schema.json";
|
||||
import getMessagesSchema from "./schemas/getMessages-schema.json";
|
||||
import getMembersSchema from "./schemas/getMembers-schema.json";
|
||||
|
||||
export const validateCreateAccount = ajv.compile(createAccountSchema);
|
||||
export const validateGetAccounts = ajv.compile(getAccountSchema);
|
||||
export const validateGetRooms = ajv.compile(getRoomSchema);
|
||||
export const validateGetMessages = ajv.compile(getMessagesSchema);
|
||||
export const validateGetMembers = ajv.compile(getMembersSchema);
|
@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { defineProps, PropType } from "vue";
|
||||
import { Address } from "@/store/models/model";
|
||||
import Badge from "./Badge.vue";
|
||||
|
||||
const props = defineProps({ address: Object as PropType<Address> });
|
||||
const value = props.address?.name ? props.address?.name : props.address?.email;
|
||||
|
@ -13,6 +13,7 @@ export interface Message {
|
||||
subject: string;
|
||||
content: string;
|
||||
date: string;
|
||||
flags: string;
|
||||
}
|
||||
|
||||
export enum LoadingState {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import API from "@/services/imapAPI";
|
||||
import { decodeEmojis } from "@/utils/string";
|
||||
import { AxiosError, AxiosResponse } from "axios";
|
||||
import { createStore, Store } from "vuex";
|
||||
import { createStore } from "vuex";
|
||||
import { Room, Account, Address, RoomType, Message, LoadingState } from "./models/model";
|
||||
|
||||
interface RoomFromBack {
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { defineProps, onMounted, ref, watch, PropType, Prop } from "vue";
|
||||
import { defineProps, onMounted, ref, watch, PropType } from "vue";
|
||||
import { decodeEmojis } from "../../utils/string";
|
||||
import { removeDuplicates } from "../../utils/array";
|
||||
import DOMPurify from "dompurify";
|
||||
@ -9,6 +9,7 @@ const props = defineProps({
|
||||
msg: Object as PropType<Message>,
|
||||
members: Array as PropType<Address[]>,
|
||||
});
|
||||
console.log(props.msg);
|
||||
const iframe = ref<HTMLIFrameElement>();
|
||||
|
||||
// todo dompurify
|
||||
@ -62,6 +63,21 @@ const displayAddresses = (addressIds: string[] | undefined): string => {
|
||||
});
|
||||
return res;
|
||||
};
|
||||
|
||||
const classes = (): string => {
|
||||
const flags = props.msg?.flags?.split(",");
|
||||
|
||||
// not flags implies no seen flag
|
||||
if (!flags) return "msg-notSeen";
|
||||
|
||||
// Important takes the priority on Seen flag
|
||||
if (flags.includes("\\Important")) {
|
||||
return "msg-important";
|
||||
} else if (!flags.includes("\\Seen")) {
|
||||
return "msg-notSeen";
|
||||
}
|
||||
return "msg-basic";
|
||||
};
|
||||
</script>
|
||||
<!-- to if to is more than me
|
||||
cc -->
|
||||
@ -89,7 +105,7 @@ const displayAddresses = (addressIds: string[] | undefined): string => {
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="content" :class="[classes()]">
|
||||
<iframe ref="iframe"></iframe>
|
||||
<div class="options">options</div>
|
||||
</div>
|
||||
@ -126,8 +142,18 @@ const displayAddresses = (addressIds: string[] | undefined): string => {
|
||||
display: flex;
|
||||
padding-top: 6px;
|
||||
flex-direction: row;
|
||||
/* background-color: #ec7a4342;
|
||||
background-color: #353c6261; */
|
||||
}
|
||||
|
||||
.msg-important {
|
||||
background-color: #ec7a4342;
|
||||
}
|
||||
|
||||
.msg-notSeen {
|
||||
background-color: #222b5b61;
|
||||
}
|
||||
|
||||
.msg-basic {
|
||||
background-color: var(--tertiary-background);
|
||||
}
|
||||
|
||||
iframe {
|
||||
@ -135,7 +161,7 @@ iframe {
|
||||
max-height: 300px;
|
||||
flex-basis: 100%;
|
||||
border: none;
|
||||
max-width: 600px; /* template width being 600px to 640px up to 750px (experiment and test) */
|
||||
max-width: 640px; /* template width being 600px to 640px up to 750px (experiment and test) */
|
||||
background-color: rgb(234, 234, 234);
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ const shouldDisplayComposer = () => {
|
||||
<Message
|
||||
v-for="(message, index) in room?.messages"
|
||||
:key="index"
|
||||
:data="message"
|
||||
:msg="message"
|
||||
:members="room?.members"
|
||||
/>
|
||||
</div>
|
||||
|
@ -1,23 +1,16 @@
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from "vue-router";
|
||||
import { defineProps } from "vue";
|
||||
import { defineProps, PropType } from "vue";
|
||||
import BaseAvatar from "../../avatars/BaseAvatar.vue";
|
||||
import Badge from "@/components/Badge.vue";
|
||||
import ThreadList from "./threads/ThreadList.vue";
|
||||
import store from "@/store/store";
|
||||
import { Room } from "@/store/models/model";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
id: Number,
|
||||
roomName: String,
|
||||
user: String,
|
||||
userId: Number,
|
||||
notSeen: Number,
|
||||
mailboxId: Number,
|
||||
threadIds: Array,
|
||||
},
|
||||
room: Object as PropType<Room>,
|
||||
});
|
||||
// console.log(props.data.threadIds);
|
||||
// console.log(props.room?.threadIds);
|
||||
|
||||
const router = useRouter();
|
||||
</script>
|
||||
@ -26,18 +19,17 @@ const router = useRouter();
|
||||
<div>
|
||||
<div
|
||||
class="room"
|
||||
@click="router.push(`/${props.data.id}`)"
|
||||
v-bind:class="store.state.activeRoom == props.data.id ? 'selected' : ''"
|
||||
@click="router.push(`/${props.room?.id}`)"
|
||||
v-bind:class="store.state.activeRoom == props.room?.id ? 'selected' : ''"
|
||||
>
|
||||
<BaseAvatar url="vue.png" />
|
||||
<div class="content">
|
||||
<div class="sender">{{ props.data.user }}</div>
|
||||
<div class="object">{{ props.data.roomName }}</div>
|
||||
<div class="sender">{{ props.room?.user }}</div>
|
||||
<div class="object">{{ props.room?.roomName }}</div>
|
||||
</div>
|
||||
{{ props.data.unseen }}
|
||||
<Badge class="badge" v-if="props.data.notSeen > 0" :value="props.data.notSeen" type="badge-number" />
|
||||
<Badge class="badge" v-if="props.room?.notSeen ?? 0 > 0" :value="props.room?.notSeen" type="badge-number" />
|
||||
</div>
|
||||
<ThreadList :threadIds="props.data.threadIds" />
|
||||
<ThreadList :threadIds="props.room?.threadIds" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<Room v-for="(room, index) in rooms()" :key="index" :data="room" />
|
||||
<Room v-for="(room, index) in rooms()" :key="index" :room="room" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user