add file chuck upload, file validation, redirect to home page incase route doesnt exist and refresh token interceptor

This commit is contained in:
Peter Maquiran
2023-11-09 11:45:04 +01:00
parent a05f85a37f
commit a16e97a59a
41 changed files with 48604 additions and 1902 deletions
+1
View File
@@ -97,3 +97,4 @@ src/app/pipes/process.service.ts
src/app/domain src/app/domain
_src/ _src/
-src -src
plugin copy
+47467 -1642
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -175,6 +175,7 @@
"rocket.chat.realtime.api.rxjs": "^2.1.1", "rocket.chat.realtime.api.rxjs": "^2.1.1",
"rxjs": "~6.6.3", "rxjs": "~6.6.3",
"rxjs-compat": "^6.6.7", "rxjs-compat": "^6.6.7",
"sanitize-filename-ts": "^1.0.2",
"sharp": "^0.30.7", "sharp": "^0.30.7",
"socket.io-client": "^2.3.0", "socket.io-client": "^2.3.0",
"tinymce": "^6.6.0", "tinymce": "^6.6.0",
+1 -1
View File
@@ -213,7 +213,7 @@ import { FirebaseX } from '@ionic-native/firebase-x/ngx'; */
FileOpener, FileOpener,
DocumentViewer, DocumentViewer,
{ provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptorService, multi: true }, { provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptorService, multi: true },
], ],
bootstrap: [AppComponent], bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA] schemas: [CUSTOM_ELEMENTS_SCHEMA]
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { MiddlewareRemoteDatasourcecService } from './middleware-remote-datasourcec.service';
describe('MiddlewareRemoteDatasourcecService', () => {
let service: MiddlewareRemoteDatasourcecService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(MiddlewareRemoteDatasourcecService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,17 @@
import { Injectable } from '@angular/core';
import { SessionStore } from "src/app/store/session.service"
import { MiddlewareServiceService } from "src/app/shared/API/middleware/middleware-service.service"
@Injectable({
providedIn: 'root'
})
export class MiddlewareRemoteDatasourcecService {
constructor(
private MiddlewareServiceService: MiddlewareServiceService,
) {}
refreshToken() {
const refreshToken = SessionStore.user.Authorization
return this.MiddlewareServiceService.refreshToken(refreshToken)
}
}
+2
View File
@@ -413,6 +413,8 @@ const routes: Routes = [
canActivate: [InactivityGuard] canActivate: [InactivityGuard]
}, },
{ path: '**', redirectTo: '/', pathMatch: 'full' },
]; ];
@NgModule({ @NgModule({
+5 -2
View File
@@ -20,7 +20,8 @@ import { RouteService } from 'src/app/services/route.service';
import { NetworkServiceService, ConnectionStatus } from 'src/app/services/network-service.service'; import { NetworkServiceService, ConnectionStatus } from 'src/app/services/network-service.service';
import { UserSession } from '../models/user.model'; import { UserSession } from '../models/user.model';
import { PermissionList } from '../models/permission/permissionList'; import { PermissionList } from '../models/permission/permissionList';
// import { ChunkService } from "src/app/services/stream/chunk.service"
// import { StreamService } from "src/app/services/stream/stream.service"
import { Plugins } from '@capacitor/core'; import { Plugins } from '@capacitor/core';
const { App } = Plugins; const { App } = Plugins;
@@ -92,7 +93,9 @@ export class HomePage implements OnInit {
private NetworkServiceService: NetworkServiceService, private NetworkServiceService: NetworkServiceService,
public eventService: EventsService, public eventService: EventsService,
public ActiveTabService: ActiveTabService, public ActiveTabService: ActiveTabService,
private RoleIdService: RoleIdService private RoleIdService: RoleIdService,
// private ChunkService: ChunkService,
// private StreamService: StreamService
) { ) {
if (SessionStore.exist) { if (SessionStore.exist) {
this.user = SessionStore.user; this.user = SessionStore.user;
-1
View File
@@ -228,7 +228,6 @@
<!-- {{year.yearInfo.yearName}} --> <!-- {{year.yearInfo.yearName}} -->
<div *ngFor="let month of year.months " class="header-day" > <div *ngFor="let month of year.months " class="header-day" >
<!-- {{ month.monthInfo.monthName | json }} --> <!-- {{ month.monthInfo.monthName | json }} -->
<!-- <hr> -->
<div *ngFor="let day of month.days " class="EventListBox-container" > <div *ngFor="let day of month.days " class="EventListBox-container" >
@@ -32,6 +32,10 @@ import { SessionStore } from 'src/app/store/session.service';
import { ViewMediaPage } from 'src/app/modals/view-media/view-media.page'; import { ViewMediaPage } from 'src/app/modals/view-media/view-media.page';
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera'; import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
import { RouteService } from 'src/app/services/route.service'; import { RouteService } from 'src/app/services/route.service';
import { FileValidatorService } from "src/app/services/file/file-validator.service"
import { sanitize } from "sanitize-filename-ts";
@Component({ @Component({
selector: 'app-group-messages', selector: 'app-group-messages',
templateUrl: './group-messages.page.html', templateUrl: './group-messages.page.html',
@@ -104,6 +108,7 @@ export class GroupMessagesPage implements OnInit, AfterViewInit, OnDestroy {
private file: File, private file: File,
private fileOpener: FileOpener, private fileOpener: FileOpener,
public RouteService: RouteService, public RouteService: RouteService,
private FileValidatorService: FileValidatorService
) { ) {
this.ChatSystemService.getUser() this.ChatSystemService.getUser()
@@ -507,7 +512,7 @@ export class GroupMessagesPage implements OnInit, AfterViewInit, OnDestroy {
"mimeType": audioFile.value.mimeType, "mimeType": audioFile.value.mimeType,
}, },
attachments: [{ attachments: [{
"title": fileName, "title": sanitize(fileName),
"title_link_download": true, "title_link_download": true,
"type": "audio" "type": "audio"
}], }],
@@ -828,37 +833,45 @@ export class GroupMessagesPage implements OnInit, AfterViewInit, OnDestroy {
const file: any = await this.fileService.getFileFromDevice(types); const file: any = await this.fileService.getFileFromDevice(types);
const fileName = file.name
if (file.type != "application/img" && file.type != "image/png" && file.type != "image/jpeg" && file.type != "image/gif") { const validation = this.FileValidatorService.fileNameValidation(fileName)
const encodedData = btoa(JSON.stringify(await this.getBase64(file).catch((error) => { if(validation.isOk) {
console.error(error);
})));
const blob = this.fileService.base64toBlob(encodedData, file.type)
const formData = new FormData(); if (file.type != "application/img" && file.type != "image/png" && file.type != "image/jpeg" && file.type != "image/gif") {
formData.append('blobFile', blob);
const encodedData = btoa(JSON.stringify(await this.getBase64(file).catch((error) => {
console.error(error);
})));
const blob = this.fileService.base64toBlob(encodedData, file.type)
const formData = new FormData();
formData.append('blobFile', blob);
this.ChatSystemService.getGroupRoom(roomId).send({ this.ChatSystemService.getGroupRoom(roomId).send({
file: { file: {
"type": file.type, "type": file.type,
"guid": '', "guid": '',
}, },
attachments: [{ attachments: [{
"title": file.name, "title": sanitize(fileName),
"name": file.name, "name": sanitize(fileName),
// "text": "description", // "text": "description",
"title_link_download": false, "title_link_download": false,
}], }],
temporaryData: formData, temporaryData: formData,
attachmentsModelData: { attachmentsModelData: {
fileBase64: encodedData fileBase64: encodedData
} }
}); });
} else {
}
} else { } else {
this.toastService._badRequest("ficheiro invalido")
} }
} }
+39 -36
View File
@@ -33,6 +33,10 @@ import { Filesystem, Directory } from '@capacitor/filesystem';
import { NewEventPage } from '../../agenda/new-event/new-event.page'; import { NewEventPage } from '../../agenda/new-event/new-event.page';
import { NotificationsService } from 'src/app/services/notifications.service'; import { NotificationsService } from 'src/app/services/notifications.service';
import { RochetChatConnectorService } from 'src/app/services/chat/rochet-chat-connector.service' import { RochetChatConnectorService } from 'src/app/services/chat/rochet-chat-connector.service'
import { FileValidatorService } from "src/app/services/file/file-validator.service"
import { sanitize } from "sanitize-filename-ts";
const IMAGE_DIR = 'stored-images'; const IMAGE_DIR = 'stored-images';
@@ -117,6 +121,7 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
private fileOpener: FileOpener, private fileOpener: FileOpener,
private router: Router, private router: Router,
public RochetChatConnectorService: RochetChatConnectorService, public RochetChatConnectorService: RochetChatConnectorService,
private FileValidatorService: FileValidatorService
) { ) {
try { try {
@@ -413,12 +418,13 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
this.audioRecorded = `data:${recordData.value.mimeType};base64,${recordData?.value?.recordDataBase64}`; this.audioRecorded = `data:${recordData.value.mimeType};base64,${recordData?.value?.recordDataBase64}`;
} }
//Converting base64 to blob //Converting base64 to blob
const encodedData = btoa(this.audioRecorded); const encodedData = btoa(this.audioRecorded);
const blob = this.fileService.base64toBlob(encodedData, recordData.value.mimeType) const blob = this.fileService.base64toBlob(encodedData, recordData.value.mimeType)
const validation = await this.FileValidatorService.validateAudioFromBlob(blob)
const formData = new FormData(); const formData = new FormData();
formData.append("blobFile", blob); formData.append("blobFile", blob);
@@ -429,7 +435,7 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
"mimeType": audioFile.value.mimeType, "mimeType": audioFile.value.mimeType,
}, },
attachments: [{ attachments: [{
"title": fileName, "title": sanitize(fileName),
"title_link_download": true, "title_link_download": true,
"type": "audio" "type": "audio"
}], }],
@@ -759,7 +765,6 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
} }
async addFileToChatMobile(types: typeof FileType[]) { async addFileToChatMobile(types: typeof FileType[]) {
console.log('add image from gallery')
const roomId = this.roomId const roomId = this.roomId
const file = await Camera.getPhoto({ const file = await Camera.getPhoto({
@@ -852,43 +857,41 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
formData.append('blobFile', blob); formData.append('blobFile', blob);
console.log('add file', fileBase64) */ console.log('add file', fileBase64) */
const fileName = file.name
const validation = this.FileValidatorService.fileNameValidation(fileName)
if (file.type != "application/img" && file.type != "image/png" && file.type != "image/jpeg" && file.type != "image/gif") { if(validation.isOk) {
console.log('TYPE', file.type)
const encodedData = btoa(JSON.stringify(await this.getBase64(file).catch((error) => { const encodedData = btoa(JSON.stringify(await this.getBase64(file).catch((error) => {
console.error(error); console.error(error);
}))); })));
console.log(encodedData) console.log(encodedData)
const blob = this.fileService.base64toBlob(encodedData, file.type) const blob = this.fileService.base64toBlob(encodedData, file.type)
console.log('BLOB BLOB', blob)
const formData = new FormData();
formData.append('blobFile', blob);
/* console.log('add file', fileBase64) */
this.ChatSystemService.getDmRoom(roomId).send({
file: {
"type": file.type,
"guid": '',
},
attachments: [{
"title": file.name,
"name": file.name,
// "text": "description",
"title_link_download": false,
}],
temporaryData: formData,
attachmentsModelData: {
fileBase64: encodedData,
}
});
} else {
console.log('file not supported')
}
const formData = new FormData();
formData.append('blobFile', blob);
/* console.log('add file', fileBase64) */
this.ChatSystemService.getDmRoom(roomId).send({
file: {
"type": file.type,
"guid": '',
},
attachments: [{
"title": sanitize(fileName),
"name": sanitize(fileName),
// "text": "description",
"title_link_download": false,
}],
temporaryData: formData,
attachmentsModelData: {
fileBase64: encodedData,
}
});
} else {
this.toastService._badRequest("ficheiro invalido")
}
} }
@@ -21,27 +21,27 @@ export class AttendeesPageModal implements OnInit {
eventPersons: EventPerson[]; eventPersons: EventPerson[];
adding: "intervenient" | "CC"; adding: "intervenient" | "CC";
currentPath = window.location.pathname; currentPath = window.location.pathname;
taskParticipants:EventPerson[] = []; taskParticipants:EventPerson[] = [];
taskParticipantsCc:EventPerson[] = []; taskParticipantsCc:EventPerson[] = [];
loggeduser: LoginUserRespose; loggeduser: LoginUserRespose;
@Input() loggedAttendSon: boolean; @Input() loggedAttendSon: boolean;
taskType: any; taskType: any;
constructor( constructor(
private modalCtrl: ModalController, private modalCtrl: ModalController,
private contactsService: ContactsService, private contactsService: ContactsService,
private navParams: NavParams, private navParams: NavParams,
private modalController: ModalController, private modalController: ModalController,
public ThemeService: ThemeService, public ThemeService: ThemeService,
private router: Router,) { private router: Router,) {
this.adding = this.navParams.get('adding'); this.adding = this.navParams.get('adding');
this.taskParticipants = this.navParams.get('taskParticipants'); this.taskParticipants = this.navParams.get('taskParticipants');
this.taskParticipantsCc = this.navParams.get('taskParticipantsCc'); this.taskParticipantsCc = this.navParams.get('taskParticipantsCc');
this.taskType = this.navParams.get('taskType'); this.taskType = this.navParams.get('taskType');
this.loggeduser = SessionStore.user; this.loggeduser = SessionStore.user;
} }
@@ -49,7 +49,7 @@ export class AttendeesPageModal implements OnInit {
ngOnInit() { ngOnInit() {
console.log('Pesquisa de contactos current path1',this.router.url) console.log('Pesquisa de contactos current path1',this.router.url)
this.fetchContacts(""); this.fetchContacts("");
if(this.taskParticipants == null || this.taskParticipants == undefined){ if(this.taskParticipants == null || this.taskParticipants == undefined){
this.taskParticipants = []; this.taskParticipants = [];
} }
@@ -57,9 +57,9 @@ export class AttendeesPageModal implements OnInit {
if(this.taskParticipantsCc == null || this.taskParticipantsCc == undefined){ if(this.taskParticipantsCc == null || this.taskParticipantsCc == undefined){
this.taskParticipantsCc = []; this.taskParticipantsCc = [];
} }
} }
ngOnChanges(event) {} ngOnChanges(event) {}
save(){ save(){
@@ -81,16 +81,16 @@ export class AttendeesPageModal implements OnInit {
filterSearchList(itm: EventPerson): boolean { filterSearchList(itm: EventPerson): boolean {
const result = this.taskParticipants.concat( this.taskParticipantsCc).find((contact, index)=>{ const result = this.taskParticipants.concat( this.taskParticipantsCc).find((contact, index)=>{
if(contact.Name.toLocaleLowerCase() == itm.Name.toLocaleLowerCase() && contact.EmailAddress.toLocaleLowerCase() == itm.EmailAddress.toLocaleLowerCase()){ if(contact.Name.toLocaleLowerCase() == itm.Name.toLocaleLowerCase() && contact.EmailAddress.toLocaleLowerCase() == itm.EmailAddress.toLocaleLowerCase()){
index = index; index = index;
return contact; return contact;
} }
}) })
return undefined == result; return undefined == result;
} }
remove(itm: EventPerson) { remove(itm: EventPerson) {
@@ -103,7 +103,7 @@ export class AttendeesPageModal implements OnInit {
return contact; return contact;
} }
return false; return false;
}); });
} else if (this.adding == "CC") { } else if (this.adding == "CC") {
@@ -114,7 +114,7 @@ export class AttendeesPageModal implements OnInit {
return contact; return contact;
} }
return false; return false;
}); });
} }
@@ -124,7 +124,7 @@ export class AttendeesPageModal implements OnInit {
if(this.adding == "intervenient"){ if(this.adding == "intervenient"){
itm.IsRequired = true; itm.IsRequired = true;
this.taskParticipants.push(itm); this.taskParticipants.push(itm);
} else if (this.adding == "CC") { } else if (this.adding == "CC") {
itm.IsRequired = false; itm.IsRequired = false;
this.taskParticipantsCc.push(itm); this.taskParticipantsCc.push(itm);
@@ -142,7 +142,7 @@ export class AttendeesPageModal implements OnInit {
const index: number = result.findIndex((cont) => { const index: number = result.findIndex((cont) => {
return cont.EmailAddress.toLocaleLowerCase() == attendee.EmailAddress.toLocaleLowerCase() return cont.EmailAddress.toLocaleLowerCase() == attendee.EmailAddress.toLocaleLowerCase()
}); });
result.splice(index, 1); result.splice(index, 1);
}); });
@@ -153,16 +153,16 @@ export class AttendeesPageModal implements OnInit {
this.showLoader = false; this.showLoader = false;
} else { } else {
this.contacts = result; this.contacts = result;
console.log('Attendes Email',this.loggeduser.Email) // console.log('Attendes Email',this.loggeduser.Email)
let filterLoggedUserEmail = this.contacts.filter(item => item.EmailAddress.toLocaleLowerCase() != this.loggeduser.Email.toLocaleLowerCase()) let filterLoggedUserEmail = this.contacts.filter(item => item.EmailAddress.toLocaleLowerCase() != this.loggeduser.Email.toLocaleLowerCase())
if(this.taskType == 0 || this.taskType == 1){ if(this.taskType == 0 || this.taskType == 1){
filterLoggedUserEmail = this.contacts.filter(item => item.IsPR == false) filterLoggedUserEmail = this.contacts.filter(item => item.IsPR == false)
} }
console.log('Attendes Email', filterLoggedUserEmail) // console.log('Attendes Email', filterLoggedUserEmail)
let filterEmptyEmail = filterLoggedUserEmail.filter(item => item.EmailAddress.toLocaleLowerCase() != "") let filterEmptyEmail = filterLoggedUserEmail.filter(item => item.EmailAddress.toLocaleLowerCase() != "")
this.contacts = filterEmptyEmail; this.contacts = filterEmptyEmail;
console.log('Attendes Email', this.contacts) //console.log('Attendes Email', this.contacts)
this.showLoader = false; this.showLoader = false;
} }
@@ -183,4 +183,4 @@ export class AttendeesPageModal implements OnInit {
}).reverse() }).reverse()
} }
} }
@@ -23,7 +23,7 @@ import { PublicationFolderService } from 'src/app/store/publication-folder.servi
import { RouteService } from 'src/app/services/route.service'; import { RouteService } from 'src/app/services/route.service';
import { FileService } from 'src/app/services/functions/file.service'; import { FileService } from 'src/app/services/functions/file.service';
import { readAndCompressImage } from 'browser-image-resizer'; import { readAndCompressImage } from 'browser-image-resizer';
import { FileValidatorService } from "src/app/services/file/file-validator.service"
const config = { const config = {
quality: 0.5, quality: 0.5,
maxWidth: 800, maxWidth: 800,
@@ -100,7 +100,8 @@ export class NewPublicationPage implements OnInit {
private httpErrorHandle: HttpErrorHandle, private httpErrorHandle: HttpErrorHandle,
public PublicationFolderService: PublicationFolderService, public PublicationFolderService: PublicationFolderService,
private RouteService: RouteService, private RouteService: RouteService,
public FileService: FileService public FileService: FileService,
private FileValidatorService: FileValidatorService
) { ) {
this.publicationType = this.navParams.get('publicationType'); this.publicationType = this.navParams.get('publicationType');
@@ -132,18 +133,29 @@ export class NewPublicationPage implements OnInit {
source: CameraSource.Camera source: CameraSource.Camera
}); });
this.capturedImage = 'data:image/jpeg;base64,' +capturedImage.base64String; this.capturedImage = 'data:image/jpeg;base64,' +capturedImage.base64String;
this.capturedImageTitle = 'foto'; this.capturedImageTitle = 'foto';
const compressedImage = await this.compressImageBase64( const validation = await this.FileValidatorService.ValidateImage(this.capturedImage)
this.capturedImage,
800, // maxWidth if(validation.isOk) {
800, // maxHeight
0.9 // quality validation.value
).then((picture) => {
console.log('taked: ', picture) const compressedImage = await this.compressImageBase64(
this.capturedImage = picture this.capturedImage,
}); 800, // maxWidth
800, // maxHeight
0.9 // quality
).then((picture) => {
this.capturedImage = picture
});
} else if(validation.isError) {
validation.error
this.toastService._badRequest("imagem invalida")
}
} }
async laodPicture() { async laodPicture() {
@@ -156,15 +168,23 @@ export class NewPublicationPage implements OnInit {
this.capturedImage = 'data:image/jpeg;base64,' +capturedImage.base64String; this.capturedImage = 'data:image/jpeg;base64,' +capturedImage.base64String;
this.capturedImageTitle = 'foto'; this.capturedImageTitle = 'foto';
const compressedImage = await this.compressImageBase64(
this.capturedImage,
800, // maxWidth const validation = await this.FileValidatorService.ValidateImage(this.capturedImage)
800, // maxHeight if(validation.isOk) {
0.9 // quality const compressedImage = await this.compressImageBase64(
).then((picture) => { this.capturedImage,
console.log('Selected: ', picture) 800, // maxWidth
this.capturedImage = picture 800, // maxHeight
}); 0.9 // quality
).then((picture) => {
console.log('Selected: ', picture)
this.capturedImage = picture
});
} else {
this.toastService._badRequest("imagem invalida")
}
} }
@@ -189,30 +209,6 @@ export class NewPublicationPage implements OnInit {
}); });
/* // in use
async laodPicture() {
const capturedImage = await Camera.getPhoto({
quality: 90,
// allowEditing: true,
resultType: CameraResultType.Uri,
source: CameraSource.Photos
});
const response = await fetch(capturedImage.webPath!);
const blob = await response.blob();
this.convertBlobToBase64Worker.postMessage(blob);
this.convertBlobToBase64Worker.onmessage = async (oEvent)=> {
this.capturedImage = oEvent.data
this.capturedImageTitle = 'foto'
}
} */
runValidation() { runValidation() {
this.validateFrom = true this.validateFrom = true
} }
@@ -316,7 +312,7 @@ export class NewPublicationPage implements OnInit {
else { else {
const date = formatDate(new Date(), 'yyyy-MM-dd HH:mm:ss') const date = formatDate(new Date(), 'yyyy-MM-dd HH:mm:ss')
this.publication = { this.publication = {
DateIndex: date, DateIndex: date,
DocumentId: null, DocumentId: null,
@@ -332,7 +328,7 @@ export class NewPublicationPage implements OnInit {
const loader = this.toastService.loading() const loader = this.toastService.loading()
try { try {
await this.publications.CreatePublication(this.folderId, this.publication).toPromise(); await this.publications.CreatePublication(this.folderId, this.publication).toPromise();
this.close(); this.close();
this.httpErrorHandle.httpsSucessMessagge('Criar publicação') this.httpErrorHandle.httpsSucessMessagge('Criar publicação')
@@ -373,7 +369,7 @@ export class NewPublicationPage implements OnInit {
this.publicationTitle = 'Editar Publicação'; this.publicationTitle = 'Editar Publicação';
this.pub = this.navParams.get('publication'); this.pub = this.navParams.get('publication');
} }
} }
@@ -383,11 +379,11 @@ export class NewPublicationPage implements OnInit {
//this.imgResultBeforeCompress = image;s //this.imgResultBeforeCompress = image;s
this.imageCompress.getOrientation(this.capturedImage).then((orientation) => { this.imageCompress.getOrientation(this.capturedImage).then((orientation) => {
this.imageCompress.compressFile(this.capturedImage, orientation, 90, 90).then( this.imageCompress.compressFile(this.capturedImage, orientation, 90, 90).then(
result => { result => {
this.capturedImage = result; this.capturedImage = result;
} }
); );
@@ -403,36 +399,37 @@ export class NewPublicationPage implements OnInit {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const image = new (window as any).Image(); const image = new (window as any).Image();
image.src = base64String; image.src = base64String;
image.onload = async () => { image.onload = async () => {
const canvas = document.createElement('canvas'); const canvas = document.createElement('canvas');
let newWidth = image.width; let newWidth = image.width;
let newHeight = image.height; let newHeight = image.height;
if (newWidth > maxWidth) { if (newWidth > maxWidth) {
newHeight *= maxWidth / newWidth; newHeight *= maxWidth / newWidth;
newWidth = maxWidth; newWidth = maxWidth;
} }
if (newHeight > maxHeight) { if (newHeight > maxHeight) {
newWidth *= maxHeight / newHeight; newWidth *= maxHeight / newHeight;
newHeight = maxHeight; newHeight = maxHeight;
} }
canvas.width = newWidth; canvas.width = newWidth;
canvas.height = newHeight; canvas.height = newHeight;
const context = canvas.getContext('2d'); const context = canvas.getContext('2d');
context?.drawImage(image, 0, 0, newWidth, newHeight); context?.drawImage(image, 0, 0, newWidth, newHeight);
const compressedBase64 = canvas.toDataURL('image/jpeg', quality); const compressedBase64 = canvas.toDataURL('image/jpeg', quality);
resolve(compressedBase64); resolve(compressedBase64);
}; };
image.onerror = (error) => { image.onerror = (error) => {
reject(error); reject(error);
}; };
}); });
} }
} }
@@ -19,8 +19,11 @@
<div class="title-content width-100 d-flex justify-space-between"> <div class="title-content width-100 d-flex justify-space-between">
<div class="div-title flex-grow-1"> <div class="div-title flex-grow-1">
<ion-label class="title font-25-em">Acções</ion-label> <ion-label class="title font-25-em">Acções</ion-label>
<!-- <div>
<input type="file" (change)="onFileSelect($event)" />
</div> -->
</div> </div>
<div class="div-icon"> <div class="div-icon">
<button *ngIf="p.userPermission([p.permissionList.Actions.create])" title="Adicionar nova ação presidencial" class="btn-no-color" (click)="AddPublicationFolder()"> <button *ngIf="p.userPermission([p.permissionList.Actions.create])" title="Adicionar nova ação presidencial" class="btn-no-color" (click)="AddPublicationFolder()">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " slot="icon-only" src='assets/images/icons-add.svg'></ion-icon> <ion-icon *ngIf="ThemeService.currentTheme == 'default' " slot="icon-only" src='assets/images/icons-add.svg'></ion-icon>
+63 -12
View File
@@ -14,6 +14,10 @@ import { ToastService } from 'src/app/services/toast.service';
import { ThemeService } from 'src/app/services/theme.service' import { ThemeService } from 'src/app/services/theme.service'
import { PermissionService } from 'src/app/services/permission.service'; import { PermissionService } from 'src/app/services/permission.service';
import { Storage } from '@ionic/storage'; import { Storage } from '@ionic/storage';
import { ChunkService } from 'src/app/services/stream/chunk.service'
import { StreamService } from 'src/app/services/stream/stream.service'
import { HttpClient, HttpHeaders, HttpEventType } from '@angular/common/http';
// import { ActionModel } from 'src/app/models/beast-orm'; // import { ActionModel } from 'src/app/models/beast-orm';
@@ -68,6 +72,9 @@ export class PublicationsPage implements OnInit {
public ThemeService: ThemeService, public ThemeService: ThemeService,
public p: PermissionService, public p: PermissionService,
private storage: Storage, private storage: Storage,
private ChunkService: ChunkService,
private StreamService:StreamService,
private http: HttpClient,
) { ) {
this.months = ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"]; this.months = ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"];
@@ -143,11 +150,11 @@ export class PublicationsPage implements OnInit {
this.publications.GetPublicationFolderList().subscribe(async res => { this.publications.GetPublicationFolderList().subscribe(async res => {
this.showLoader = false; this.showLoader = false;
const folders: PublicationFolder[] = this.getPublicationFolderMap(res) const folders: PublicationFolder[] = this.getPublicationFolderMap(res)
this.publicationsEventFolderList = folders.filter((e)=>e.ActionType == 'Evento') this.publicationsEventFolderList = folders.filter((e)=>e.ActionType == 'Evento')
await this.storage.set('actionsEvents', this.publicationsEventFolderList); await this.storage.set('actionsEvents', this.publicationsEventFolderList);
this.showLoader = false; this.showLoader = false;
// (async ()=> { // (async ()=> {
@@ -189,35 +196,79 @@ export class PublicationsPage implements OnInit {
} }
async getFromDB() { async getFromDB() {
//const folders: PublicationFolder[] = await ActionModel.all() //const folders: PublicationFolder[] = await ActionModel.all()
//this.showLoader = false; //this.showLoader = false;
// this.publicationsEventFolderList = folders // this.publicationsEventFolderList = folders
this.storage.get('actionsEvents').then((events = []) => { this.storage.get('actionsEvents').then((events = []) => {
if(Array.isArray(events)) { if(Array.isArray(events)) {
const folders: PublicationFolder[] = this.getPublicationFolderMap(events) const folders: PublicationFolder[] = this.getPublicationFolderMap(events)
this.showLoader = false; this.showLoader = false;
this.publicationsEventFolderList = folders this.publicationsEventFolderList = folders
} }
}); });
this.storage.get('actionsViagens').then((viagens = []) => { this.storage.get('actionsViagens').then((viagens = []) => {
if(Array.isArray(viagens)) { if(Array.isArray(viagens)) {
const folders: PublicationFolder[] = this.getPublicationFolderMap(viagens) const folders: PublicationFolder[] = this.getPublicationFolderMap(viagens)
this.publicationsTravelFolderList = folders this.publicationsTravelFolderList = folders
this.showLoader = false; this.showLoader = false;
} }
}); });
} }
async onFileSelect(event: any) {
const file:File = event.target.files[0];
const chunkSize = 1024 * 1024; // Adjust the chunk size as needed
const chunks = [];
let offset = 0;
let i = 0;
let j = 0;
function count () {
j++
return j
}
while (offset < file.size) {
const chunk = file.slice(offset, offset + chunkSize);
const reader = new FileReader();
reader.onload = async () => {
const headers = new HttpHeaders()
.append('X-File-Name', "fileName")
.append('X-File-Extension', "mp4")
.append('X-File-Content-Length', i.toString())
.append('X-File-Index', count().toString());
const a = new Uint8Array(reader.result as ArrayBuffer)
await this.http.post('http://localhost:3001/upload', a.buffer, { headers, responseType: 'blob' }).toPromise();
};
reader.readAsArrayBuffer(chunk);
offset += chunkSize;
i++;
}
}
async editAction(folderId?: string) { async editAction(folderId?: string) {
const modal = await this.modalController.create({ const modal = await this.modalController.create({
component: EditActionPage, component: EditActionPage,
@@ -244,7 +295,7 @@ export class PublicationsPage implements OnInit {
if(error.status == 0) { if(error.status == 0) {
this.toastService._badRequest('Sem acesso à internet. Por favor verifique sua conexão') this.toastService._badRequest('Sem acesso à internet. Por favor verifique sua conexão')
} else { } else {
this.toastService._badRequest() this.toastService._badRequest()
} }
} }
@@ -252,7 +303,7 @@ export class PublicationsPage implements OnInit {
loader.remove() loader.remove()
this.refreshing() this.refreshing()
} }
} }
async AddPublicationFolder(item?: any) { async AddPublicationFolder(item?: any) {
@@ -270,7 +321,7 @@ export class PublicationsPage implements OnInit {
cssClass: 'new-action modal modal-desktop', cssClass: 'new-action modal modal-desktop',
backdropDismiss: false backdropDismiss: false
}); });
modal.onDidDismiss().then(() => { modal.onDidDismiss().then(() => {
this.getActions(); this.getActions();
}); });
@@ -336,7 +387,7 @@ export class PublicationsPage implements OnInit {
cssClass: 'new-action modal modal-desktop', cssClass: 'new-action modal modal-desktop',
backdropDismiss: false backdropDismiss: false
}); });
modal.onDidDismiss(); modal.onDidDismiss();
await modal.present(); await modal.present();
@@ -470,7 +521,7 @@ export class PublicationsPage implements OnInit {
}, },
//translucent: true //translucent: true
}); });
modal.onDidDismiss().then(res => { modal.onDidDismiss().then(res => {
if (res['data'] == 'edit') { if (res['data'] == 'edit') {
this.closeDesktopComponent(); this.closeDesktopComponent();
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { MiddlewareRepositoryService } from './middleware-repository.service';
describe('MiddlewareRepositoryService', () => {
let service: MiddlewareRepositoryService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(MiddlewareRepositoryService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,18 @@
import { Injectable } from '@angular/core';
import { MiddlewareRemoteDatasourcecService } from "src/app/datasource/middleware/remote/middleware-remote-datasourcec.service"
@Injectable({
providedIn: 'root'
})
export class MiddlewareRepositoryService {
constructor(
private MiddlewareRemoteDatasourcecService: MiddlewareRemoteDatasourcecService,
) {}
refreshToken() {
return this.MiddlewareRemoteDatasourcecService.refreshToken()
}
}
+10
View File
@@ -0,0 +1,10 @@
export type Either<T, E> = { isOk: true; isError: false, value: T } | { isOk: false; isError: true, error: E };
export function ok<T, E>(value: T): Either<T, E> {
return { isOk: true, isError: false, value };
}
export function error<T, E>(error: E): Either<T, E> {
return { isOk: false, isError: true, error};
}
+1 -1
View File
@@ -64,7 +64,7 @@ export class AuthService {
const data = SessionStore.getDataFromLocalStorage(); const data = SessionStore.getDataFromLocalStorage();
if (!data?.user?.Authorization && SessionStore?.user?.Authorization) { if (!data?.user?.Authorization && SessionStore?.user?.Authorization) {
window.location.reload(); // window.location.reload();
} }
if (window['all-process-gabinete']) { if (window['all-process-gabinete']) {
@@ -21,22 +21,22 @@ export class AttendeesPageModal implements OnInit {
eventPersons: EventPerson[]; eventPersons: EventPerson[];
adding: "intervenient" | "CC"; adding: "intervenient" | "CC";
currentPath = window.location.pathname; currentPath = window.location.pathname;
taskParticipants:EventPerson[] = []; taskParticipants:EventPerson[] = [];
taskParticipantsCc:EventPerson[] = []; taskParticipantsCc:EventPerson[] = [];
loggeduser: LoginUserRespose; loggeduser: LoginUserRespose;
@Input() loggedAttendSon: boolean; @Input() loggedAttendSon: boolean;
taskType: any; taskType: any;
constructor( constructor(
private modalCtrl: ModalController, private modalCtrl: ModalController,
private contactsService: ContactsService, private contactsService: ContactsService,
private navParams: NavParams, private navParams: NavParams,
private modalController: ModalController, private modalController: ModalController,
public ThemeService: ThemeService, public ThemeService: ThemeService,
private router: Router,) { private router: Router,) {
this.adding = this.navParams.get('adding'); this.adding = this.navParams.get('adding');
this.taskParticipants = this.navParams.get('taskParticipants'); this.taskParticipants = this.navParams.get('taskParticipants');
this.taskParticipantsCc = this.navParams.get('taskParticipantsCc'); this.taskParticipantsCc = this.navParams.get('taskParticipantsCc');
@@ -48,7 +48,7 @@ export class AttendeesPageModal implements OnInit {
ngOnInit() { ngOnInit() {
console.log('Pesquisa de contactos current path2',this.router.url) console.log('Pesquisa de contactos current path2',this.router.url)
this.fetchContacts(""); this.fetchContacts("");
if(this.taskParticipants == null || this.taskParticipants == undefined){ if(this.taskParticipants == null || this.taskParticipants == undefined){
this.taskParticipants = []; this.taskParticipants = [];
} }
@@ -56,9 +56,9 @@ export class AttendeesPageModal implements OnInit {
if(this.taskParticipantsCc == null || this.taskParticipantsCc == undefined){ if(this.taskParticipantsCc == null || this.taskParticipantsCc == undefined){
this.taskParticipantsCc = []; this.taskParticipantsCc = [];
} }
} }
ngOnChanges(event) {} ngOnChanges(event) {}
save(){ save(){
@@ -80,16 +80,16 @@ export class AttendeesPageModal implements OnInit {
filterSearchList(itm: EventPerson): boolean { filterSearchList(itm: EventPerson): boolean {
const result = this.taskParticipants.concat( this.taskParticipantsCc).find((contact, index)=>{ const result = this.taskParticipants.concat( this.taskParticipantsCc).find((contact, index)=>{
if(contact.Name.toLocaleLowerCase() == itm.Name.toLocaleLowerCase() && contact.EmailAddress.toLocaleLowerCase() == itm.EmailAddress.toLocaleLowerCase()){ if(contact.Name.toLocaleLowerCase() == itm.Name.toLocaleLowerCase() && contact.EmailAddress.toLocaleLowerCase() == itm.EmailAddress.toLocaleLowerCase()){
index = index; index = index;
return contact; return contact;
} }
}) })
return undefined == result; return undefined == result;
} }
remove(itm: EventPerson){ remove(itm: EventPerson){
@@ -102,7 +102,7 @@ export class AttendeesPageModal implements OnInit {
return contact; return contact;
} }
return false; return false;
}); });
} else if (this.adding == "CC") { } else if (this.adding == "CC") {
@@ -113,7 +113,7 @@ export class AttendeesPageModal implements OnInit {
return contact; return contact;
} }
return false; return false;
}); });
} }
@@ -123,7 +123,7 @@ export class AttendeesPageModal implements OnInit {
if(this.adding == "intervenient"){ if(this.adding == "intervenient"){
itm.IsRequired = true; itm.IsRequired = true;
this.taskParticipants.push(itm); this.taskParticipants.push(itm);
} else if (this.adding == "CC") { } else if (this.adding == "CC") {
itm.IsRequired = false; itm.IsRequired = false;
this.taskParticipantsCc.push(itm); this.taskParticipantsCc.push(itm);
@@ -141,7 +141,7 @@ export class AttendeesPageModal implements OnInit {
const index: number = result.findIndex((cont) => { const index: number = result.findIndex((cont) => {
return cont.EmailAddress.toLocaleLowerCase() == attendee.EmailAddress.toLocaleLowerCase() return cont.EmailAddress.toLocaleLowerCase() == attendee.EmailAddress.toLocaleLowerCase()
}); });
result.splice(index, 1); result.splice(index, 1);
}); });
} }
@@ -151,12 +151,12 @@ export class AttendeesPageModal implements OnInit {
this.showLoader = false; this.showLoader = false;
} else { } else {
this.contacts = result; this.contacts = result;
console.log('Attendes Email',this.loggeduser.Email) // console.log('Attendes Email',this.loggeduser.Email)
let filterLoggedUserEmail = this.contacts.filter(item => item.EmailAddress.toLocaleLowerCase() != this.loggeduser.Email.toLocaleLowerCase()) let filterLoggedUserEmail = this.contacts.filter(item => item.EmailAddress.toLocaleLowerCase() != this.loggeduser.Email.toLocaleLowerCase())
console.log('Attendes Email', filterLoggedUserEmail) // console.log('Attendes Email', filterLoggedUserEmail)
let filterEmptyEmail = filterLoggedUserEmail.filter(item => item.EmailAddress.toLocaleLowerCase() != "") let filterEmptyEmail = filterLoggedUserEmail.filter(item => item.EmailAddress.toLocaleLowerCase() != "")
this.contacts = filterEmptyEmail; this.contacts = filterEmptyEmail;
console.log('Attendes Email', this.contacts) // console.log('Attendes Email', this.contacts)
this.showLoader = false; this.showLoader = false;
} }
@@ -164,4 +164,4 @@ export class AttendeesPageModal implements OnInit {
); );
} }
} }
+1 -1
View File
@@ -25,7 +25,7 @@ export class FileLoaderService {
// input.onchange = () => { // input.onchange = () => {
// // you can use this method to get file and perform respective operations // // you can use this method to get file and perform respective operations
// let files = Array.from(input.files); // let files = Array.from(input.files);
// //
// }; // };
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { FileValidatorService } from './file-validator.service';
describe('FileValidatorService', () => {
let service: FileValidatorService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(FileValidatorService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,173 @@
import { Injectable } from '@angular/core';
import { Either, error, ok} from "src/app/services/Either"
import * as Sentry from '@sentry/capacitor';
@Injectable({
providedIn: 'root'
})
export class FileValidatorService {
forbiddenExtensions = [
".exe",
".bat",
".sh",
".jar",
".js",
".vbs",
".ps1",
".ini",
".config",
".zip",
".rar",
".tar.gz",
".7z",
".cab",
".sql",
".bak",
".htaccess",
".htpasswd",
".scr",
".pif",
".com",
".msi",
".dll",
".sys",
".ini",
".docm",
".xlsm",
".pptm",
".rtf",
".so",
".dylib",
".dat",
".log",
".conf",
".php",
".py",
".rb",
];
constructor() { }
private base64ToBlob(base64) {
const binaryData = atob(base64);
const arrayBuffer = new ArrayBuffer(binaryData.length);
const view = new Uint8Array(arrayBuffer);
for (let i = 0; i < binaryData.length; i++) {
view[i] = binaryData.charCodeAt(i);
}
return new Blob([arrayBuffer], { type: 'video/mp4' }); // Adjust the type as per your video format
}
ValidateImage(base64Image: string): Promise<Either<true, Event>> {
const imageDiv: HTMLImageElement = document.createElement("img")
return new Promise((resolve, reject) => {
imageDiv.onload = () => {
resolve(ok(true))
}
imageDiv.onerror = (e: Event) => {
resolve(error(e))
Sentry.captureMessage('FileValidatorService invalid image content');
}
imageDiv.src = base64Image
})
}
ValidateVideoFromBase64Data(base64Data: string) : Promise<Either<true, false>> {
const blob = this.base64ToBlob(base64Data);
return new Promise((resolve, reject) => {
const videoElement = document.createElement('video');
videoElement.src = URL.createObjectURL(blob);
// Add event listeners to handle video load and error
videoElement.addEventListener('loadeddata', () => {
resolve(ok(true))
// You can also perform additional validation checks here if needed.
});
videoElement.addEventListener('error', () => {
resolve(ok(true))
Sentry.captureMessage('FileValidatorService invalid video content');
});
})
}
ValidateVideoFromBlob(blob: Blob) : Promise<Either<true, false>> {
return new Promise((resolve, reject) => {
const videoElement = document.createElement('video');
videoElement.src = URL.createObjectURL(blob);
// Add event listeners to handle video load and error
videoElement.addEventListener('loadeddata', () => {
resolve(ok(true))
// You can also perform additional validation checks here if needed.
});
videoElement.addEventListener('error', () => {
resolve(ok(true))
Sentry.captureMessage('FileValidatorService invalid video content');
});
})
}
validateAudioFromBlob(blob: Blob): Promise<Either<true, false>> {
return new Promise((resolve, reject) => {
const audioElement = new Audio();
// Add event listeners to handle audio load and error
audioElement.addEventListener('canplaythrough', () => {
console.log('Valid audio');
resolve(ok(true))
// You can also perform additional validation checks here if needed.
});
audioElement.addEventListener('error', () => {
console.log('Invalid audio');
resolve(error(false))
});
audioElement.src = URL.createObjectURL(blob);
})
}
hasDuplicateExtension(filename): Either<true, false> {
// Use a regular expression to match multiple consecutive dots
const duplicateExtensionRegex = /\.\.+/;
const a = duplicateExtensionRegex.test(filename);
if(a) {
return ok(true)
} else {
Sentry.captureMessage('FileValidatorService invalid filename '+ filename);
return error(false)
}
}
fileNameValidation(fileName): Either<true, false> {
const fileExtension = fileName.slice(((fileName.lastIndexOf(".") - 1) >>> 0) + 2);
const found = this.forbiddenExtensions.includes(`.${fileExtension.toLowerCase()}`);
if(found) {
return error(false)
} else {
return ok(true)
}
}
}
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { HttpServiceService } from './http-service.service';
describe('HttpServiceService', () => {
let service: HttpServiceService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(HttpServiceService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,43 @@
import { HttpClient, HttpContext, HttpHeaders, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
// import { Result, err, ok } from 'neverthrow'
import { tap, shareReplay, catchError } from "rxjs/operators";
import { Observable, of } from "rxjs";
@Injectable({
providedIn: 'root'
})
export class HttpServiceService {
constructor(private http: HttpClient) {}
put(url: string, body: any | null, options: Options): Observable<any> {
return this.http.put(url, body, options as any).pipe(
tap((response) => {
// Handle success response if needed
}),
catchError((error) => {
// Handle error response if needed
return of(error);
})
);
}
}
interface Options {
headers?: HttpHeaders | {
[header: string]: string | string[];
};
context?: HttpContext;
observe?: 'body';
params?: HttpParams | {
[param: string]: string | number | boolean | ReadonlyArray<string | number | boolean>;
};
reportProgress?: boolean;
responseType?: 'arraybuffer';
withCredentials?: boolean;
}
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { TokenInterceptorService } from './token-interceptor.service';
describe('TokenInterceptorService', () => {
let service: TokenInterceptorService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(TokenInterceptorService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,81 @@
import { Injectable } from "@angular/core";
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor,
HttpErrorResponse,
HTTP_INTERCEPTORS,
} from "@angular/common/http";
import { Observable, throwError, BehaviorSubject } from "rxjs";
import { catchError, filter, take, switchMap } from "rxjs/operators";
import { SessionStore } from "src/app/store/session.service";
import { MiddlewareRepositoryService } from "src/app/repository/middleWare/middleware-repository.service"
@Injectable({
providedIn: 'root'
})
export class TokenInterceptorService {
private isRefreshing = false;
private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(
null
);
constructor(private middlewareRepositoryService: MiddlewareRepositoryService) {
this.middlewareRepositoryService = middlewareRepositoryService
}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (SessionStore.user.Authorization) {
request = this.addToken(request, SessionStore.user.Authorization);
}
return next.handle(request).pipe(
catchError((error) => {
if (error instanceof HttpErrorResponse && error.status === 401) {
return this.handle401Error(request, next);
} else {
return throwError(error);
}
})
) as any
}
private addToken(request: HttpRequest<any>, token: string) {
return request.clone({
setHeaders: {
Authorization: `Bearer ${token}`,
},
});
}
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
return this.middlewareRepositoryService.refreshToken().pipe(
switchMap((token: any) => {
this.isRefreshing = false;
this.refreshTokenSubject.next(token['result'].accessToken);
return next.handle(this.addToken(request, token['result'].accessToken));
})
);
} else {
return this.refreshTokenSubject.pipe(
filter((token) => token != null),
take(1),
switchMap((jwt) => {
return next.handle(this.addToken(request, jwt));
})
);
}
}
}
+15 -10
View File
@@ -239,17 +239,22 @@ export class NotificationsService {
} }
); );
} else { } else {
navigator.serviceWorker.onmessage = (event) => { try {
console.log('Mensagem recebida do Service Worker:', event.data.data);
let object = {
notification: event.data
}
if (event.data.notificationClicked) { navigator.serviceWorker.onmessage = (event) => {
console.log('Notificação push do Firebase clicada em segundo plano!'); console.log('Mensagem recebida do Service Worker:', event.data.data);
this.notificatinsRoutes(object) let object = {
} notification: event.data
}; }
if (event.data.notificationClicked) {
console.log('Notificação push do Firebase clicada em segundo plano!');
this.notificatinsRoutes(object)
}
};
} catch(e) {
console.log(e)
}
} }
} }
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { ChunkService } from './chunk.service';
describe('ChunkService', () => {
let service: ChunkService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ChunkService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
+140
View File
@@ -0,0 +1,140 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class ChunkService {
constructor() {
window["ChunkService"] = this
// const a = this.generateChunkFile({} as any, {} as any)
}
generateChunkFile(base64: string, chunkSizeInBytes: number): File[] {
const byteCharacters = atob(base64); // Decode base64 data to binary
const chunks: File[] = [];
let start = 0;
function createChunk(): File {
const chunkSize = Math.min(chunkSizeInBytes, byteCharacters.length - start);
const chunkData = byteCharacters.slice(start, start + chunkSize);
start += chunkSize;
// console.log({chunkData})
const chunkArray = new Uint8Array(chunkData.length);
for (let i = 0; i < chunkData.length; i++) {
chunkArray[i] = chunkData.charCodeAt(i);
// console.log('chunkData.charCodeAt', chunkData.charCodeAt(i))
}
// console.log({Uint8Array:"Uint8Array", chunkArray})
const blob = new Blob([chunkArray]);
// console.log('blob')
// console.log('File')
return new File([blob], `chunk_${chunks.length + 1}`);
}
while (start < byteCharacters.length) {
chunks.push(createChunk());
}
return chunks;
}
generateChunkOfUint8Array(base64: string, chunkSizeInBytes: number): Uint8Array[] {
const byteCharacters = atob(base64); // Decode base64 data to binary
const chunks: Uint8Array[] = [];
let start = 0;
function createChunk(): Uint8Array {
const chunkSize = Math.min(chunkSizeInBytes, byteCharacters.length - start);
const chunkData = byteCharacters.slice(start, start + chunkSize);
start += chunkSize;
const chunkArray = new Uint8Array(chunkData.length);
for (let i = 0; i < chunkData.length; i++) {
chunkArray[i] = chunkData.charCodeAt(i);
}
return chunkArray;
}
while (start < byteCharacters.length) {
chunks.push(createChunk());
}
return chunks;
}
async uploadChunk(file: File): Promise<any> {
// Read and upload the file in chunks (as you've previously implemented)
const chunkSize = 1024 * 500; // Adjust the chunk size as needed
const chunks = [];
let offset = 0;
let i = 0
while (offset < file.size) {
console.log(offset)
const chunk = file.slice(offset, offset + chunkSize);
const reader = new FileReader();
reader.onload = () => {
chunks[i] = new Uint8Array(reader.result as ArrayBuffer)
};
reader.readAsArrayBuffer(chunk);
offset += chunkSize;
}
return chunks
}
onFileSelect(event: any, chunkSizeInBytes):Promise<Blob[]> {
const file:File = event.target.files[0];
const filename = file.name;
//const chunkSize = 1024 * 1024; // 1 MB chunks (adjust as needed)
const chunkSize = chunkSizeInBytes
return new Promise((resolve, reject) => {
// Read and upload chunks
const fileReader = new FileReader();
fileReader.onload = (e) => {
const arrayBuffer = e.target.result as ArrayBuffer;
const blob = new Blob([new Uint8Array(arrayBuffer)]);
const totalChunks = Math.ceil(file.size / chunkSize);
const chunks: Blob[] = []
for (let i = 1; i <= totalChunks; i++) {
const start = (i - 1) * chunkSize;
const end = i * chunkSize;
const chunk = blob.slice(start, end);
chunks.push(chunk)
}
resolve(chunks)
};
fileReader.readAsArrayBuffer(file);
})
}
generateChunkFromBase64(base64: string, chunkSizeInBytes: number) {
const byteCharacters = atob(base64); // Decode base64 data to binary
const chunkArray = [];
for (let offset = 0; offset < byteCharacters.length; offset += chunkSizeInBytes) {
const chunkData = byteCharacters.slice(offset, offset + chunkSizeInBytes);
chunkArray.push(chunkData);
}
return chunkArray;
}
}
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { StreamService } from './stream.service';
describe('StreamService', () => {
let service: StreamService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(StreamService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
+112
View File
@@ -0,0 +1,112 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpEventType } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class StreamService {
constructor(
private http: HttpClient,
) {
window["StreamService"] = this
}
async uploadFile() {
const API_URL = 'http://localhost:3000/upload'; // Replace with your server URL
const filePath = 'path/to/large-file.zip'; // Replace with the path to your file
const fileName = 'my-file'; // Specify your desired filename
const fileExtension = 'zip'; // Specify the file extension
const headers = new HttpHeaders()
.append('X-File-Name', fileName)
.append('X-File-Extension', fileExtension);
const file = await this.readFileInChunks(filePath);
const chunkSize = 1024 * 1024; // 1 MB chunk size (adjust as needed)
for (let offset = 0; offset < file.length; offset += chunkSize) {
const chunk = file.slice(offset, offset + chunkSize);
// await this.uploadChunk(API_URL, chunk, headers);
}
console.log('Upload completed.');
}
async readFileInChunks(filePath: string): Promise<Uint8Array> {
const response = await fetch(filePath);
const reader = response.body.getReader();
const chunks: Uint8Array[] = [];
let done = false;
while (!done) {
const { value, done: isDone } = await reader.read();
if (!isDone) {
chunks.push(value);
}
done = isDone;
}
return new Uint8Array([].concat(...chunks.map((chunk) => Array.from(chunk))));
}
async uploadChunk(url: string, chunks: Uint8Array[], fileName, fileExtension): Promise<void> {
let i = 1
console.log('123', chunks.length)
for(const chunk of chunks) {
try {
console.log("iterate")
const headers = new HttpHeaders()
.append('X-File-Name', fileName)
.append('X-File-Extension', fileExtension)
.append('X-File-Content-Length', chunks.length.toString())
.append('X-File-Index', i.toString())
await this.http.post('http://localhost:3001/upload', chunk.buffer, { headers, responseType: 'blob' }).toPromise();
i++
} catch (error) {
console.error('Upload error:', error);
}
}
}
async uploadChunkNoLoop(url: string, chunk: Uint8Array, fileName, fileExtension, i, length): Promise<void> {
console.log("iterate")
const headers = new HttpHeaders()
.append('X-File-Name', fileName)
.append('X-File-Extension', fileExtension)
.append('X-File-Content-Length', length)
.append('X-File-Index', i.toString())
await this.http.post('http://localhost:3001/upload', chunk.buffer, { headers, responseType: 'blob' }).toPromise();
}
uploadChunk1(chunk: Blob, chunkNumber: number, totalChunks: number, filename: string) {
console.log(chunk)
const headers = new HttpHeaders()
.append('X-File-Name', filename)
.append('X-File-Content-Length', totalChunks.toString())
.append('X-File-Index', chunkNumber.toString())
return this.http.post('http://localhost:3001/upload-chunk', Blob, { headers, responseType: 'blob' });
}
}
// const text = 'Hello, World00120301010asdf1002sdf 0fsdfasf0001230 12300!\n';
// const base64 = btoa(text);
// let chunks = window["ChunkService"].generateChunkOfUint8Array(base64, 8)
// window.StreamService.uploadChunk("", chunks, "peter12", "txt")
+5
View File
@@ -0,0 +1,5 @@
export interface refreshToken {
Authorization: string,
refreshToken: null
}
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { MiddlewareServiceService } from './middleware-service.service';
describe('MiddlewareServiceService', () => {
let service: MiddlewareServiceService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(MiddlewareServiceService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,23 @@
import { Injectable } from '@angular/core';
import { environment } from 'src/environments/environment';
import { HttpServiceService } from 'src/app/services/http/http-service.service';
import { Observable } from "rxjs";
import { refreshToken } from "./interface"
@Injectable({
providedIn: 'root'
})
export class MiddlewareServiceService {
constructor(
private HttpServiceService: HttpServiceService,
) {}
refreshToken(refreshToken: string): Observable<refreshToken> {
const data = {
refreshToken: refreshToken
}
return this.HttpServiceService.put(environment.apiURL + "UserAuthentication/RefreshToken", data, {})
}
}
@@ -29,6 +29,7 @@ import { FileOpener } from '@awesome-cordova-plugins/file-opener/ngx';
import { SessionStore } from 'src/app/store/session.service'; import { SessionStore } from 'src/app/store/session.service';
import { ViewMediaPage } from 'src/app/modals/view-media/view-media.page'; import { ViewMediaPage } from 'src/app/modals/view-media/view-media.page';
import { PermissionService } from 'src/app/services/permission.service'; import { PermissionService } from 'src/app/services/permission.service';
import { FileValidatorService } from "src/app/services/file/file-validator.service"
@Component({ @Component({
selector: 'app-group-messages', selector: 'app-group-messages',
@@ -105,6 +106,7 @@ export class GroupMessagesPage implements OnInit, OnChanges, AfterViewInit, OnDe
private platform: Platform, private platform: Platform,
private fileOpener: FileOpener, private fileOpener: FileOpener,
public p: PermissionService, public p: PermissionService,
private FileValidatorService: FileValidatorService
) { ) {
this.ChatSystemService.getUser() this.ChatSystemService.getUser()
@@ -901,9 +903,11 @@ export class GroupMessagesPage implements OnInit, OnChanges, AfterViewInit, OnDe
const file: any = await this.fileService.getFileFromDevice(types); const file: any = await this.fileService.getFileFromDevice(types);
const fileName = file.name
const validation = this.FileValidatorService.fileNameValidation(fileName)
if (file.type != "application/img" && file.type != "image/png" && file.type != "image/jpeg" && file.type != "image/gif") { if(validation.isOk) {
const encodedData = btoa(JSON.stringify(await this.getBase64(file).catch ((error) => { const encodedData = btoa(JSON.stringify(await this.getBase64(file).catch ((error) => {
console.error(error); console.error(error);
@@ -946,9 +950,8 @@ export class GroupMessagesPage implements OnInit, OnChanges, AfterViewInit, OnDe
fileBase64: fileBase64, fileBase64: fileBase64,
} }
}) })
} else { } else {
this.toastService._badRequest("ficheiro invalido")
} }
+11 -8
View File
@@ -33,6 +33,7 @@ import { Howl } from 'howler';
import { ViewMediaPage } from 'src/app/modals/view-media/view-media.page'; import { ViewMediaPage } from 'src/app/modals/view-media/view-media.page';
import { ChatMessageDebuggingPage } from 'src/app/shared/popover/chat-message-debugging/chat-message-debugging.page'; import { ChatMessageDebuggingPage } from 'src/app/shared/popover/chat-message-debugging/chat-message-debugging.page';
import { PermissionService } from 'src/app/services/permission.service'; import { PermissionService } from 'src/app/services/permission.service';
import { FileValidatorService } from "src/app/services/file/file-validator.service"
const IMAGE_DIR = 'stored-images'; const IMAGE_DIR = 'stored-images';
@@ -100,7 +101,7 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
roomName: any; roomName: any;
isAdmin = false; isAdmin = false;
roomCountDownDate: string; roomCountDownDate: string;
constructor( constructor(
public popoverController: PopoverController, public popoverController: PopoverController,
@@ -122,6 +123,7 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
private platform: Platform, private platform: Platform,
private fileOpener: FileOpener, private fileOpener: FileOpener,
public p: PermissionService, public p: PermissionService,
private FileValidatorService: FileValidatorService
) { ) {
// update // update
this.checkAudioPermission() this.checkAudioPermission()
@@ -172,8 +174,8 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
this.audioPermissionStatus = permissionStatus.state this.audioPermissionStatus = permissionStatus.state
permissionStatus.onchange = (data: any) => { permissionStatus.onchange = (data: any) => {
// //
// //
} }
} }
@@ -510,7 +512,7 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
} }
getChatMembers() { getChatMembers() {
// //
// this.showLoader = true; // this.showLoader = true;
// this.chatService.getMembers(this.roomId).subscribe(res => { // this.chatService.getMembers(this.roomId).subscribe(res => {
@@ -835,11 +837,12 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
const roomId = this.roomId const roomId = this.roomId
const file: any = await this.fileService.getFileFromDevice(types); const file: any = await this.fileService.getFileFromDevice(types);
console.log(file.type)
const fileName = file.name
const validation = this.FileValidatorService.fileNameValidation(fileName)
if (file.type != "application/img" && file.type != "image/png" && file.type != "image/jpeg" && file.type != "image/gif") { if(validation.isOk) {
const encodedData = btoa(JSON.stringify(await this.getBase64(file).catch((error) => { const encodedData = btoa(JSON.stringify(await this.getBase64(file).catch((error) => {
console.error(error); console.error(error);
@@ -886,12 +889,12 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
fileBase64: fileBase64, fileBase64: fileBase64,
} }
}) })
} else { } else {
this.toastService._badRequest("ficheiro invalido")
} }
} }
_getBase64(file) { _getBase64(file) {
@@ -59,7 +59,7 @@ export class AttendeePage implements OnInit {
this.LtaskParticipants = removeDuplicate(this.taskParticipants); this.LtaskParticipants = removeDuplicate(this.taskParticipants);
this.LtaskParticipantsCc = removeDuplicate(this.taskParticipantsCc); this.LtaskParticipantsCc = removeDuplicate(this.taskParticipantsCc);
this.loggeduser = SessionStore.user; this.loggeduser = SessionStore.user;
} }
ngOnChanges() { ngOnChanges() {
@@ -137,7 +137,7 @@ export class AttendeePage implements OnInit {
itm.IsRequired = false; itm.IsRequired = false;
this.LtaskParticipantsCc.push(itm); this.LtaskParticipantsCc.push(itm);
} else { } else {
// //
} }
// run only in gabinete digital // run only in gabinete digital
@@ -247,15 +247,15 @@ export class AttendeePage implements OnInit {
this.showLoader = false; this.showLoader = false;
} else { } else {
this.contacts = this.sort(result as any); this.contacts = this.sort(result as any);
console.log('Attendes Email', this.loggeduser.Email) // console.log('Attendes Email', this.loggeduser.Email)
let filterLoggedUserEmail = this.contacts.filter(item => item.EmailAddress.toLocaleLowerCase() != this.loggeduser.Email.toLocaleLowerCase()) let filterLoggedUserEmail = this.contacts.filter(item => item.EmailAddress.toLocaleLowerCase() != this.loggeduser.Email.toLocaleLowerCase())
if(this.taskType == 0 || this.taskType == 1){ if(this.taskType == 0 || this.taskType == 1){
filterLoggedUserEmail = this.contacts.filter(item => item.IsPR == false) filterLoggedUserEmail = this.contacts.filter(item => item.IsPR == false)
} }
console.log('Attendes Email', filterLoggedUserEmail) // console.log('Attendes Email', filterLoggedUserEmail)
let filterEmptyEmail = filterLoggedUserEmail.filter(item => item.EmailAddress.toLocaleLowerCase() != "") let filterEmptyEmail = filterLoggedUserEmail.filter(item => item.EmailAddress.toLocaleLowerCase() != "")
this.contacts = filterEmptyEmail; this.contacts = filterEmptyEmail;
console.log('Attendes Email', this.contacts) // console.log('Attendes Email', this.contacts)
this.showLoader = false; this.showLoader = false;
} }
@@ -271,4 +271,4 @@ export class AttendeePage implements OnInit {
} }
} }
} }
@@ -10,6 +10,7 @@ import { ThemeService } from 'src/app/services/theme.service';
import { Camera, CameraResultType, CameraSource} from '@capacitor/camera'; import { Camera, CameraResultType, CameraSource} from '@capacitor/camera';
import { HttpErrorHandle } from 'src/app/services/http-error-handle.service'; import { HttpErrorHandle } from 'src/app/services/http-error-handle.service';
import { PublicationFolderService } from 'src/app/store/publication-folder.service'; import { PublicationFolderService } from 'src/app/store/publication-folder.service';
import { FileValidatorService } from "src/app/services/file/file-validator.service"
@Component({ @Component({
selector: 'app-new-publication', selector: 'app-new-publication',
@@ -19,7 +20,7 @@ import { PublicationFolderService } from 'src/app/store/publication-folder.servi
export class NewPublicationPage implements OnInit { export class NewPublicationPage implements OnInit {
showLoader: boolean; showLoader: boolean;
pub: Publication = new Publication(); pub: Publication = new Publication();
publicationTitle:string; publicationTitle:string;
imgUrl:any; imgUrl:any;
@@ -50,11 +51,12 @@ export class NewPublicationPage implements OnInit {
private toastService: ToastService, private toastService: ToastService,
public ThemeService: ThemeService, public ThemeService: ThemeService,
private httpErroHandle: HttpErrorHandle, private httpErroHandle: HttpErrorHandle,
public PublicationFolderService: PublicationFolderService public PublicationFolderService: PublicationFolderService,
private FileValidatorService: FileValidatorService
) { ) {
this.publicationTitle = 'Nova Publicação'; this.publicationTitle = 'Nova Publicação';
} }
ngOnInit() { ngOnInit() {
@@ -79,7 +81,7 @@ export class NewPublicationPage implements OnInit {
getPublicationDetail() { getPublicationDetail() {
if(this.publicationType != '2') { if(this.publicationType != '2') {
this.showLoader = true; this.showLoader = true;
this.publications.GetPublicationById(this.documentId).subscribe( res => { this.publications.GetPublicationById(this.documentId).subscribe( res => {
this.publication = { this.publication = {
@@ -97,7 +99,7 @@ export class NewPublicationPage implements OnInit {
this.showLoader = false; this.showLoader = false;
}, () => { }, () => {
this.showLoader = false; this.showLoader = false;
this.goBack() this.goBack()
}); });
} }
@@ -114,16 +116,24 @@ export class NewPublicationPage implements OnInit {
this.capturedImage = 'data:image/jpeg;base64,' +capturedImage.base64String; this.capturedImage = 'data:image/jpeg;base64,' +capturedImage.base64String;
this.capturedImageTitle = 'foto'; this.capturedImageTitle = 'foto';
const compressedImage = await this.compressImageBase64( const validation = await this.FileValidatorService.ValidateImage(this.capturedImage)
this.capturedImage,
800, // maxWidth if(validation.isOk) {
800, // maxHeight
0.9 // quality const compressedImage = await this.compressImageBase64(
).then((picture) => { this.capturedImage,
console.log('taked: ', picture) 800, // maxWidth
this.capturedImage = picture 800, // maxHeight
}); 0.9 // quality
).then((picture) => {
console.log('taked: ', picture)
this.capturedImage = picture
});
} else {
this.toastService._badRequest("imagem invalida")
}
} }
async laodPicture() { async laodPicture() {
@@ -136,15 +146,24 @@ export class NewPublicationPage implements OnInit {
this.capturedImage = 'data:image/jpeg;base64,' +capturedImage.base64String; this.capturedImage = 'data:image/jpeg;base64,' +capturedImage.base64String;
this.capturedImageTitle = 'foto'; this.capturedImageTitle = 'foto';
const compressedImage = await this.compressImageBase64(
this.capturedImage, const validation = await this.FileValidatorService.ValidateImage(this.capturedImage)
800, // maxWidth
800, // maxHeight if(validation.isOk) {
0.9 // quality
).then((picture) => { const compressedImage = await this.compressImageBase64(
console.log('Selected: ', picture) this.capturedImage,
this.capturedImage = picture 800, // maxWidth
}); 800, // maxHeight
0.9 // quality
).then((picture) => {
console.log('Selected: ', picture)
this.capturedImage = picture
});
} else {
this.toastService._badRequest("imagem invalida")
}
} }
@@ -176,20 +195,20 @@ export class NewPublicationPage implements OnInit {
if(this.Form.invalid) { if(this.Form.invalid) {
return false return false
} else { } else {
} }
if(this.publicationType == '3') { if(this.publicationType == '3') {
if(!this.publication?.OriginalFileName || !this.pub.OriginalFileName) { if(!this.publication?.OriginalFileName || !this.pub.OriginalFileName) {
if(this.pub?.OriginalFileName) { if(this.pub?.OriginalFileName) {
console.log('this.pub',this.pub) console.log('this.pub',this.pub)
} }
throw('no this.publication.OriginalFileName') throw('no this.publication.OriginalFileName')
} }
const loader = this.toastService.loading() const loader = this.toastService.loading()
if(this.capturedImage != '') { if(this.capturedImage != '') {
@@ -208,7 +227,7 @@ export class NewPublicationPage implements OnInit {
} }
else if (!this.PublicationFolderService.PublicationHasImage(this.publication)) { // else if (!this.PublicationFolderService.PublicationHasImage(this.publication)) { //
this.publication = { this.publication = {
DateIndex: this.publication.DateIndex, DateIndex: this.publication.DateIndex,
DocumentId:this.publication.DocumentId, DocumentId:this.publication.DocumentId,
@@ -244,7 +263,7 @@ export class NewPublicationPage implements OnInit {
console.log({response}) console.log({response})
this.goBack(); this.goBack();
} catch (error) { } catch (error) {
this.httpErroHandle.httpStatusHandle(error) this.httpErroHandle.httpStatusHandle(error)
if(error.status == 404) { if(error.status == 404) {
@@ -255,7 +274,7 @@ export class NewPublicationPage implements OnInit {
loader.remove() loader.remove()
} }
} }
else { else {
@@ -275,16 +294,16 @@ export class NewPublicationPage implements OnInit {
const loader = this.toastService.loading() const loader = this.toastService.loading()
try { try {
await this.publications.CreatePublication(this.publication.ProcessId, this.publication).toPromise() await this.publications.CreatePublication(this.publication.ProcessId, this.publication).toPromise()
if(this.publicationTitle == '1') { if(this.publicationTitle == '1') {
} else if (this.publicationTitle == '2') { } else if (this.publicationTitle == '2') {
this.httpErroHandle.httpsSucessMessagge('Criar publicação') this.httpErroHandle.httpsSucessMessagge('Criar publicação')
} else if (this.publicationTitle == '3') { } else if (this.publicationTitle == '3') {
this.httpErroHandle.httpsSucessMessagge('Editar publicação') this.httpErroHandle.httpsSucessMessagge('Editar publicação')
} }
this.goBackToViewPublications.emit(); this.goBackToViewPublications.emit();
} catch (error) { } catch (error) {
@@ -337,32 +356,32 @@ export class NewPublicationPage implements OnInit {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const image = new (window as any).Image(); const image = new (window as any).Image();
image.src = base64String; image.src = base64String;
image.onload = async () => { image.onload = async () => {
const canvas = document.createElement('canvas'); const canvas = document.createElement('canvas');
let newWidth = image.width; let newWidth = image.width;
let newHeight = image.height; let newHeight = image.height;
if (newWidth > maxWidth) { if (newWidth > maxWidth) {
newHeight *= maxWidth / newWidth; newHeight *= maxWidth / newWidth;
newWidth = maxWidth; newWidth = maxWidth;
} }
if (newHeight > maxHeight) { if (newHeight > maxHeight) {
newWidth *= maxHeight / newHeight; newWidth *= maxHeight / newHeight;
newHeight = maxHeight; newHeight = maxHeight;
} }
canvas.width = newWidth; canvas.width = newWidth;
canvas.height = newHeight; canvas.height = newHeight;
const context = canvas.getContext('2d'); const context = canvas.getContext('2d');
context?.drawImage(image, 0, 0, newWidth, newHeight); context?.drawImage(image, 0, 0, newWidth, newHeight);
const compressedBase64 = canvas.toDataURL('image/jpeg', quality); const compressedBase64 = canvas.toDataURL('image/jpeg', quality);
resolve(compressedBase64); resolve(compressedBase64);
}; };
image.onerror = (error) => { image.onerror = (error) => {
reject(error); reject(error);
}; };
+19 -13
View File
@@ -1,46 +1,52 @@
// inspire by https://github.com/hc-oss/use-indexeddb // inspire by https://github.com/hc-oss/use-indexeddb
export class IndexedDBConnector { export class IndexedDBConnector {
static connect(config) { static connect(config) {
return new Promise((resolve, reject) => { return new Promise(async (resolve, reject) => {
const idbInstance = indexedDB || self.indexedDB || self.mozIndexedDB || self.webkitIndexedDB || self.msIndexedDB; const idbInstance = indexedDB || self.indexedDB || self.mozIndexedDB || self.webkitIndexedDB || self.msIndexedDB;
if (idbInstance) { if (idbInstance) {
const request = idbInstance.open(config.databaseName, config.version); const request = idbInstance.open(config.databaseName, config.version);
request.onsuccess = () => { request.onsuccess = () => {
resolve(request.result); return resolve(request.result);
}; };
request.onerror = (e) => { request.onerror = (e) => {
reject(e.target.error.name); reject(e.target.error.name);
}; };
request.onupgradeneeded = async (e) => { request.onupgradeneeded = async (e) => {
await this.migrate(config); const db = e.target.result;
return await this.connect(config); await this.runMigrations(db, config);
resolve(await this.connect(config))
}; };
// request.onblocked = async (e: any) => { // request.onblocked = async (e: any) => {
// reject(e.target.error.name); // reject(e.target.error.name);
// } // }
} }
else { else {
reject("IDBDatabase not supported inside webworker"); reject("IDBDatabase not supported inside webworker");
} }
}); });
} }
static migrate(config) { static migrate(config) {
return new Promise((resolve, reject) => { return new Promise(async (resolve, reject) => {
const idbInstance = indexedDB || self.indexedDB || self.mozIndexedDB || self.webkitIndexedDB || self.msIndexedDB; const idbInstance = indexedDB || self.indexedDB || self.mozIndexedDB || self.webkitIndexedDB || self.msIndexedDB;
if (idbInstance) { if (idbInstance) {
const request = idbInstance.open(config.databaseName, config.version); const request = idbInstance.open(config.databaseName, config.version);
request.onsuccess = () => { request.onsuccess = async () => {
// request.result.close() resolve(false);
resolve(false);
}; };
request.onerror = (e) => { request.onerror = (e) => {
//console.log("error migrate " + config.databaseName)
//console.log("error migrate"+ e.target.error)
reject(e.target.error.name); reject(e.target.error.name);
}; };
request.onupgradeneeded = async (e) => { request.onupgradeneeded = async (e) => {
const db = e.target.result; // console.log("migrate create db")
await this.runMigrations(db, config); const db = e.target.result;
db.close(); await this.runMigrations(db, config);
resolve(true); db.close();
resolve(true);
}; };
} }
else { else {
+19 -1
View File
@@ -13,5 +13,23 @@
</rule> </rule>
</rules> </rules>
</rewrite> </rewrite>
<httpProtocol>
<customHeaders>
<!-- Iframe on the same host only -->
<add name="X-Frame-Options" value="SAMEORIGIN" />
<!-- HTTPS on -->
<add name="Strict-Transport-Security" value="max-age=31536000" />
<add name="Referrer-Policy" value="no-referrer" />
<add name="X-Content-Type-Options" value="nosniff" />
<!-- <add e="Expect-CT" value="enforce, max-age=86400" /> -->
<!-- Permision -->
<add name="Permissions-Policy" value="camera=(), microphone=()" />
<!-- same origin only -->
<add name="Cross-Origin-Embedder-Policy" value="require-corp" />
<!-- same origin only -->
<add name="Cross-Origin-Opener-Policy" value="same-origin" />
</customHeaders>
</httpProtocol>
</system.webServer> </system.webServer>
</configuration> </configuration>