export DB, Event creation, broker with city/state
This commit is contained in:
parent
60866473f7
commit
68d2615f0f
|
|
@ -2,6 +2,7 @@
|
|||
/dist
|
||||
/node_modules
|
||||
/build
|
||||
/data
|
||||
|
||||
# Logs
|
||||
logs
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import { PaymentModule } from './payment/payment.module';
|
|||
import { RequestDurationMiddleware } from './request-duration/request-duration.middleware';
|
||||
import { SelectOptionsModule } from './select-options/select-options.module';
|
||||
import { UserModule } from './user/user.module';
|
||||
import { EventModule } from './event/event.module';
|
||||
// const __filename = fileURLToPath(import.meta.url);
|
||||
// const __dirname = path.dirname(__filename);
|
||||
|
||||
|
|
@ -85,6 +86,7 @@ loadEnvFiles();
|
|||
AiModule,
|
||||
LogModule,
|
||||
PaymentModule,
|
||||
EventModule,
|
||||
],
|
||||
controllers: [AppController, LogController],
|
||||
providers: [AppService, FileService, JwtStrategy],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
import 'dotenv/config';
|
||||
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||
import { promises as fs } from 'fs';
|
||||
import { Pool } from 'pg';
|
||||
import * as schema from './schema';
|
||||
|
||||
// Drizzle-Tabellen-Definitionen (hier hast du bereits die Tabellen definiert, wir nehmen an, sie werden hier importiert)
|
||||
import { businesses, commercials, users } from './schema'; // Anpassen je nach tatsächlicher Struktur
|
||||
|
||||
const connectionString = process.env.DATABASE_URL;
|
||||
console.log(connectionString);
|
||||
const client = new Pool({ connectionString });
|
||||
const db = drizzle(client, { schema, logger: true });
|
||||
(async () => {
|
||||
try {
|
||||
// Abfrage der Daten für jede Tabelle
|
||||
const usersData = await db.select().from(users).execute();
|
||||
const businessesData = await db.select().from(businesses).execute();
|
||||
const commercialsData = await db.select().from(commercials).execute();
|
||||
|
||||
// Speichern der Daten in JSON-Dateien
|
||||
await fs.writeFile('./data/users_export.json', JSON.stringify(usersData, null, 2));
|
||||
console.log('Users exportiert in users.json');
|
||||
|
||||
await fs.writeFile('./data/businesses_export.json', JSON.stringify(businessesData, null, 2));
|
||||
console.log('Businesses exportiert in businesses.json');
|
||||
|
||||
await fs.writeFile('./data/commercials_export.json', JSON.stringify(commercialsData, null, 2));
|
||||
console.log('Commercials exportiert in commercials.json');
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Exportieren der Tabellen:', error);
|
||||
} finally {
|
||||
await client.end();
|
||||
}
|
||||
})();
|
||||
|
|
@ -134,10 +134,10 @@ export const commercials = pgTable(
|
|||
// });
|
||||
export const listingEvents = pgTable('listing_events', {
|
||||
id: uuid('id').primaryKey().defaultRandom().notNull(),
|
||||
listingId: uuid('listing_id').notNull(), // Assuming listings are referenced by UUID, adjust as necessary
|
||||
listingId: uuid('listing_id'), // Assuming listings are referenced by UUID, adjust as necessary
|
||||
userId: uuid('user_id'), // Nullable, if user is logged in, otherwise null
|
||||
eventType: varchar('event_type', { length: 50 }).notNull(), // 'view', 'print', 'email', 'facebook', 'x', 'linkedin', 'contact'
|
||||
eventTimestamp: timestamp('event_timestamp').defaultNow().notNull(),
|
||||
eventType: varchar('event_type', { length: 50 }), // 'view', 'print', 'email', 'facebook', 'x', 'linkedin', 'contact'
|
||||
eventTimestamp: timestamp('event_timestamp').defaultNow(),
|
||||
userIp: varchar('user_ip', { length: 45 }), // Optional if you choose to track IP in frontend or backend
|
||||
userAgent: varchar('user_agent', { length: 255 }), // Store User-Agent as string
|
||||
locationCountry: varchar('location_country', { length: 100 }), // Country from IP
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
import { Body, Controller, Headers, Ip, Post } from '@nestjs/common';
|
||||
import { ListingEvent } from 'src/models/db.model';
|
||||
import { EventService } from './event.service';
|
||||
|
||||
@Controller('event')
|
||||
export class EventController {
|
||||
constructor(private eventService: EventService) {}
|
||||
@Post()
|
||||
async createEvent(
|
||||
@Body() event: ListingEvent, // Struktur des Body-Objekts entsprechend anpassen
|
||||
@Ip() userIp: string, // IP Adresse des Clients
|
||||
@Headers('user-agent') userAgent: string, // User-Agent des Clients
|
||||
) {
|
||||
//const { listingId, userId, eventType, locationCountry, locationCity, locationLat, locationLng, referrer, additionalData } = body;
|
||||
event.userIp = userIp;
|
||||
event.userAgent = userAgent;
|
||||
this.eventService.createEvent(event);
|
||||
return { message: 'Event gespeichert' };
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { DrizzleModule } from 'src/drizzle/drizzle.module';
|
||||
import { EventController } from './event.controller';
|
||||
import { EventService } from './event.service';
|
||||
|
||||
@Module({
|
||||
imports: [DrizzleModule],
|
||||
controllers: [EventController],
|
||||
providers: [EventService],
|
||||
})
|
||||
export class EventModule {}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||
import { ListingEvent } from 'src/models/db.model';
|
||||
import * as schema from '../drizzle/schema';
|
||||
import { listingEvents, PG_CONNECTION } from '../drizzle/schema';
|
||||
@Injectable()
|
||||
export class EventService {
|
||||
constructor(
|
||||
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
|
||||
@Inject(PG_CONNECTION) private conn: NodePgDatabase<typeof schema>,
|
||||
) {}
|
||||
async createEvent(event: ListingEvent) {
|
||||
// Speichere das Event in der Datenbank
|
||||
await this.conn.insert(listingEvents).values(event).execute();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
|
||||
import { Body, Controller, Get, Ip, Param, Post } from '@nestjs/common';
|
||||
import { CountyRequest } from 'src/models/server.model';
|
||||
import { GeoService } from './geo.service';
|
||||
|
||||
|
|
@ -24,4 +24,8 @@ export class GeoController {
|
|||
findByPrefixAndStates(@Body() countyRequest: CountyRequest): any {
|
||||
return this.geoService.findCountiesStartingWith(countyRequest.prefix, countyRequest.states);
|
||||
}
|
||||
@Get('ipinfo/georesult/wysiwyg')
|
||||
fetchIpAndGeoLocation(@Ip() userIp: string): any {
|
||||
return this.geoService.fetchIpAndGeoLocation(userIp);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ export class GeoService {
|
|||
});
|
||||
return state ? result.filter(e => e.state.toLowerCase() === state.toLowerCase()) : result;
|
||||
}
|
||||
findCitiesAndStatesStartingWith(prefix: string, state?: string): Array<CityAndStateResult> {
|
||||
findCitiesAndStatesStartingWith(prefix: string): Array<CityAndStateResult> {
|
||||
const results: Array<CityAndStateResult> = [];
|
||||
|
||||
const lowercasePrefix = prefix.toLowerCase();
|
||||
|
|
@ -100,4 +100,14 @@ export class GeoService {
|
|||
getCityWithCoords(state: string, city: string): City {
|
||||
return this.geo.states.find(s => s.state_code === state).cities.find(c => c.name === city);
|
||||
}
|
||||
async fetchIpAndGeoLocation(ip: string): Promise<any> {
|
||||
const response = await fetch(`${process.env.IP_INFO_URL}/${ip}/geo?token=${process.env.IP_INFO_TOKEN}`, {
|
||||
method: 'GET',
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export class MailService {
|
|||
|
||||
async sendInquiry(mailInfo: MailInfo): Promise<void | ErrorResponse> {
|
||||
try {
|
||||
const validatedSender = SenderSchema.parse(mailInfo.sender);
|
||||
SenderSchema.parse(mailInfo.sender);
|
||||
} catch (error) {
|
||||
if (error instanceof ZodError) {
|
||||
const formattedErrors = error.errors.map(err => ({
|
||||
|
|
@ -54,7 +54,7 @@ export class MailService {
|
|||
}
|
||||
async sendRequest(mailInfo: MailInfo): Promise<void | ErrorResponse> {
|
||||
try {
|
||||
const validatedSender = SenderSchema.parse(mailInfo.sender);
|
||||
SenderSchema.parse(mailInfo.sender);
|
||||
} catch (error) {
|
||||
if (error instanceof ZodError) {
|
||||
const formattedErrors = error.errors.map(err => ({
|
||||
|
|
@ -97,7 +97,7 @@ export class MailService {
|
|||
}
|
||||
async send2Friend(shareByEMail: ShareByEMail): Promise<void | ErrorResponse> {
|
||||
try {
|
||||
const validatedSender = ShareByEMailSchema.parse(shareByEMail);
|
||||
ShareByEMailSchema.parse(shareByEMail);
|
||||
} catch (error) {
|
||||
if (error instanceof ZodError) {
|
||||
const formattedErrors = error.errors.map(err => ({
|
||||
|
|
|
|||
|
|
@ -316,3 +316,20 @@ export const ShareByEMailSchema = z.object({
|
|||
type: ListingsCategoryEnum,
|
||||
});
|
||||
export type ShareByEMail = z.infer<typeof ShareByEMailSchema>;
|
||||
|
||||
export const ListingEventSchema = z.object({
|
||||
id: z.string().uuid(), // UUID für das Event
|
||||
listingId: z.string().uuid(), // UUID für das Listing
|
||||
userId: z.string().uuid().optional().nullable(), // UUID für den Benutzer, optional, wenn kein Benutzer eingeloggt ist
|
||||
eventType: z.enum(['view', 'print', 'email', 'facebook', 'x', 'linkedin', 'contact']), // Die Event-Typen
|
||||
eventTimestamp: z.string().datetime().or(z.date()), // Der Zeitstempel des Events, kann ein String im ISO-Format oder ein Date-Objekt sein
|
||||
userIp: z.string().max(45).optional().nullable(), // IP-Adresse des Benutzers, optional
|
||||
userAgent: z.string().max(255).optional().nullable(), // User-Agent des Benutzers, optional
|
||||
locationCountry: z.string().max(100).optional().nullable(), // Land, optional
|
||||
locationCity: z.string().max(100).optional().nullable(), // Stadt, optional
|
||||
locationLat: z.string().max(20).optional().nullable(), // Latitude, als String
|
||||
locationLng: z.string().max(20).optional().nullable(), // Longitude, als String
|
||||
referrer: z.string().max(255).optional().nullable(), // Referrer URL, optional
|
||||
additionalData: z.record(z.any()).optional().nullable(), // JSON für zusätzliche Daten, z.B. soziale Medien, optional
|
||||
});
|
||||
export type ListingEvent = z.infer<typeof ListingEventSchema>;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,28 @@
|
|||
{
|
||||
"/api": {
|
||||
"target": "http://localhost:3000",
|
||||
"secure": false
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/pictures": {
|
||||
"target": "http://localhost:8080",
|
||||
"secure": false
|
||||
},
|
||||
"/ipify": {
|
||||
"target": "https://api.ipify.org",
|
||||
"secure": true,
|
||||
"changeOrigin": true,
|
||||
"pathRewrite": {
|
||||
"^/ipify": ""
|
||||
}
|
||||
},
|
||||
"/ipinfo": {
|
||||
"target": "https://ipinfo.io",
|
||||
"secure": true,
|
||||
"changeOrigin": true,
|
||||
"pathRewrite": {
|
||||
"^/ipinfo": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,8 @@ import { FooterComponent } from './components/footer/footer.component';
|
|||
import { HeaderComponent } from './components/header/header.component';
|
||||
import { MessageContainerComponent } from './components/message/message-container.component';
|
||||
import { SearchModalComponent } from './components/search-modal/search-modal.component';
|
||||
import { AuditService } from './services/audit.service';
|
||||
import { GeoService } from './services/geo.service';
|
||||
import { LoadingService } from './services/loading.service';
|
||||
import { UserService } from './services/user.service';
|
||||
|
||||
|
|
@ -35,6 +37,8 @@ export class AppComponent {
|
|||
private keycloakService: KeycloakService,
|
||||
private userService: UserService,
|
||||
private confirmationService: ConfirmationService,
|
||||
private auditService: AuditService,
|
||||
private geoService: GeoService,
|
||||
) {
|
||||
this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => {
|
||||
let currentRoute = this.activatedRoute.root;
|
||||
|
|
@ -55,6 +59,11 @@ export class AppComponent {
|
|||
}
|
||||
},
|
||||
});
|
||||
// Ensure the service fetches the IP and Geolocation only once
|
||||
// this.auditService.fetchIpAndGeoLocation();
|
||||
this.geoService.fetchIpAndGeoLocation().subscribe(data => {
|
||||
console.log(JSON.stringify(data));
|
||||
});
|
||||
}
|
||||
private async handleTokenExpiration(): Promise<void> {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ import { ValidatedInputComponent } from '../../../components/validated-input/val
|
|||
import { ValidatedNgSelectComponent } from '../../../components/validated-ng-select/validated-ng-select.component';
|
||||
import { ValidatedTextareaComponent } from '../../../components/validated-textarea/validated-textarea.component';
|
||||
import { ValidationMessagesService } from '../../../components/validation-messages.service';
|
||||
import { AuditService } from '../../../services/audit.service';
|
||||
import { HistoryService } from '../../../services/history.service';
|
||||
import { ListingsService } from '../../../services/listings.service';
|
||||
import { LogService } from '../../../services/log.service';
|
||||
import { MailService } from '../../../services/mail.service';
|
||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||
import { UserService } from '../../../services/user.service';
|
||||
|
|
@ -71,7 +71,7 @@ export class DetailsBusinessListingComponent {
|
|||
public keycloakService: KeycloakService,
|
||||
private validationMessagesService: ValidationMessagesService,
|
||||
private messageService: MessageService,
|
||||
private logService: LogService,
|
||||
private auditService: AuditService,
|
||||
public emailService: EMailService,
|
||||
) {
|
||||
this.router.events.subscribe(event => {
|
||||
|
|
@ -89,12 +89,13 @@ export class DetailsBusinessListingComponent {
|
|||
this.user = await this.userService.getByMail(this.keycloakUser.email);
|
||||
this.mailinfo = createMailInfo(this.user);
|
||||
}
|
||||
this.auditService.createEvent({ listingId: this.listing.id, eventType: 'view', eventTimestamp: new Date(), userAgent: navigator.userAgent, userId: this.user?.email });
|
||||
try {
|
||||
this.listing = await lastValueFrom(this.listingsService.getListingById(this.id, 'business'));
|
||||
this.listingUser = await this.userService.getByMail(this.listing.email);
|
||||
this.description = this.sanitizer.bypassSecurityTrustHtml(this.listing.description);
|
||||
} catch (error) {
|
||||
this.logService.log({ severity: 'error', text: error.error.message });
|
||||
this.auditService.log({ severity: 'error', text: error.error.message });
|
||||
this.router.navigate(['notfound']);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@ import { ValidatedInputComponent } from '../../../components/validated-input/val
|
|||
import { ValidatedNgSelectComponent } from '../../../components/validated-ng-select/validated-ng-select.component';
|
||||
import { ValidatedTextareaComponent } from '../../../components/validated-textarea/validated-textarea.component';
|
||||
import { ValidationMessagesService } from '../../../components/validation-messages.service';
|
||||
import { AuditService } from '../../../services/audit.service';
|
||||
import { HistoryService } from '../../../services/history.service';
|
||||
import { ImageService } from '../../../services/image.service';
|
||||
import { ListingsService } from '../../../services/listings.service';
|
||||
import { LogService } from '../../../services/log.service';
|
||||
import { MailService } from '../../../services/mail.service';
|
||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||
import { UserService } from '../../../services/user.service';
|
||||
|
|
@ -80,7 +80,7 @@ export class DetailsCommercialPropertyListingComponent {
|
|||
private ngZone: NgZone,
|
||||
private validationMessagesService: ValidationMessagesService,
|
||||
private messageService: MessageService,
|
||||
private logService: LogService,
|
||||
private auditService: AuditService,
|
||||
private emailService: EMailService,
|
||||
) {
|
||||
this.mailinfo = { sender: {}, email: '', url: environment.mailinfoUrl };
|
||||
|
|
@ -116,7 +116,7 @@ export class DetailsCommercialPropertyListingComponent {
|
|||
this.images.push(new ImageItem({ src: imageURL, thumb: imageURL }));
|
||||
});
|
||||
} catch (error) {
|
||||
this.logService.log({ severity: 'error', text: error.error.message });
|
||||
this.auditService.log({ severity: 'error', text: error.error.message });
|
||||
this.router.navigate(['notfound']);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
@for (user of users; track user) {
|
||||
<div class="bg-white rounded-lg shadow-md p-6 flex flex-col justify-between">
|
||||
<div class="flex items-start space-x-4">
|
||||
<!-- <img src="https://placehold.co/80x105" alt="Portrait of Amanda Taylor, a professional woman with long dark hair" class="rounded-md w-20 h-26 object-cover" /> -->
|
||||
@if(user.hasProfile){
|
||||
<img src="{{ env.imageBaseUrl }}/pictures/profile/{{ emailToDirName(user.email) }}.avif?_ts={{ ts }}" class="rounded-md w-20 h-26 object-cover" />
|
||||
} @else {
|
||||
|
|
@ -12,13 +11,17 @@
|
|||
}
|
||||
<div class="flex-1">
|
||||
<p class="text-sm text-gray-800 mb-2">{{ user.description }}</p>
|
||||
<h3 class="text-lg font-semibold">{{ user.firstname }} {{ user.lastname }}</h3>
|
||||
<app-customer-sub-type [customerSubType]="user.customerSubType"></app-customer-sub-type>
|
||||
<p class="text-sm text-gray-600 mt-1">{{ user.companyName }}</p>
|
||||
<h3 class="text-lg font-semibold">
|
||||
{{ user.firstname }} {{ user.lastname }}<span class="bg-gray-200 text-gray-700 text-xs font-semibold px-2 py-1 rounded ml-4">{{ user.location?.name }} - {{ user.location?.state }}</span>
|
||||
</h3>
|
||||
<div class="flex items-center space-x-2 mt-2">
|
||||
<app-customer-sub-type [customerSubType]="user.customerSubType"></app-customer-sub-type>
|
||||
<p class="text-sm text-gray-600">{{ user.companyName }}</p>
|
||||
</div>
|
||||
<div class="flex items-center justify-between my-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 flex justify-between items-center">
|
||||
<!-- <img src="https://placehold.co/35x45" alt="Lone Star Business Brokers logo" class="w-8 h-10 object-contain" /> -->
|
||||
@if(user.hasCompanyLogo){
|
||||
<img src="{{ env.imageBaseUrl }}/pictures/logo/{{ emailToDirName(user.email) }}.avif?_ts={{ ts }}" class="w-8 h-10 object-contain" />
|
||||
} @else {
|
||||
|
|
@ -30,6 +33,7 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Component } from '@angular/core';
|
|||
import { Router, RouterModule } from '@angular/router';
|
||||
import { KeycloakService } from 'keycloak-angular';
|
||||
import { User } from '../../../../../bizmatch-server/src/models/db.model';
|
||||
import { LogService } from '../../services/log.service';
|
||||
import { AuditService } from '../../services/audit.service';
|
||||
import { UserService } from '../../services/user.service';
|
||||
import { map2User } from '../../utils/utils';
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ export class SuccessComponent {
|
|||
user: User;
|
||||
maxAttemptsReached: boolean = false; // Neue Variable hinzufügen
|
||||
|
||||
constructor(private keycloakService: KeycloakService, private userService: UserService, private logService: LogService, private router: Router) {}
|
||||
constructor(private keycloakService: KeycloakService, private userService: UserService, private auditService: AuditService, private router: Router) {}
|
||||
|
||||
async ngOnInit() {
|
||||
let email = null;
|
||||
|
|
@ -35,7 +35,7 @@ export class SuccessComponent {
|
|||
|
||||
async checkSubscriptionPlan(email: string, error?: string) {
|
||||
if (!email) {
|
||||
this.logService.log({ severity: 'error', text: `Unauthorized Access to Success Page ${error}` });
|
||||
this.auditService.log({ severity: 'error', text: `Unauthorized Access to Success Page ${error}` });
|
||||
this.router.navigate(['home']);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, lastValueFrom, Observable } from 'rxjs';
|
||||
import { ListingEvent } from '../../../../bizmatch-server/src/models/db.model';
|
||||
import { LogMessage } from '../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AuditService {
|
||||
private apiBaseUrl = environment.apiBaseUrl;
|
||||
private apiKey = environment.ipinfo_token;
|
||||
// private ipifyUrl = 'https://api.ipify.org?format=json';
|
||||
// private ipInfoUrl = 'https://ipinfo.io';
|
||||
private ipifyUrl = 'https://api.ipify.org?format=json';
|
||||
private ipInfoUrl = 'https://ipinfo.io';
|
||||
|
||||
// BehaviorSubject to store the geolocation data
|
||||
private geoLocationSubject = new BehaviorSubject<any>(null);
|
||||
public geoLocation$: Observable<any> = this.geoLocationSubject.asObservable();
|
||||
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
async log(message: LogMessage): Promise<void> {
|
||||
lastValueFrom(this.http.post(`${this.apiBaseUrl}/bizmatch/log`, message));
|
||||
}
|
||||
async createEvent(event: ListingEvent): Promise<void> {
|
||||
lastValueFrom(this.http.post(`${this.apiBaseUrl}/bizmatch/event`, event));
|
||||
}
|
||||
// Function to get the IP address
|
||||
private getIpAddress(): Observable<{ ip: string }> {
|
||||
return this.http.get<{ ip: string }>(`/ipinfo?format=json`);
|
||||
}
|
||||
|
||||
// Function to get geolocation using IP address
|
||||
private getGeolocation(ip: string): Observable<any> {
|
||||
return this.http.get(`/ipinfo/${ip}?token=${this.apiKey}`);
|
||||
}
|
||||
|
||||
// Fetch IP and Geolocation only once, if not already fetched
|
||||
fetchIpAndGeoLocation(): void {
|
||||
if (!this.geoLocationSubject.getValue()) {
|
||||
this.getIpAddress().subscribe({
|
||||
next: response => {
|
||||
this.getGeolocation(response.ip).subscribe({
|
||||
next: geoData => {
|
||||
this.geoLocationSubject.next(geoData); // Store the geolocation data
|
||||
},
|
||||
error: geoError => {
|
||||
console.error('Error fetching geolocation:', geoError);
|
||||
},
|
||||
});
|
||||
},
|
||||
error: ipError => {
|
||||
console.error('Error fetching IP address:', ipError);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Method to provide the stored geolocation data
|
||||
getGeoLocationData(): Observable<any> {
|
||||
return this.geoLocation$; // Returns the observable for other components to subscribe
|
||||
}
|
||||
}
|
||||
|
|
@ -27,4 +27,7 @@ export class GeoService {
|
|||
let headers = new HttpHeaders().set('X-Hide-Loading', 'true').set('Accept-Language', 'en-US');
|
||||
return this.http.get(`${this.baseUrl}?q=${prefix},US&format=json&addressdetails=1&limit=5`, { headers }) as Observable<Place[]>;
|
||||
}
|
||||
fetchIpAndGeoLocation(): Observable<any> {
|
||||
return this.http.get(`${this.apiBaseUrl}/bizmatch/geo/ipinfo/georesult/wysiwyg`);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { LogMessage } from '../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class LogService {
|
||||
private apiBaseUrl = environment.apiBaseUrl;
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
async log(message: LogMessage): Promise<void> {
|
||||
lastValueFrom(this.http.post(`${this.apiBaseUrl}/bizmatch/log`, message));
|
||||
}
|
||||
}
|
||||
|
|
@ -11,4 +11,5 @@ export const environment_base = {
|
|||
clientId: 'bizmatch-dev',
|
||||
redirectUri: 'https://dev.bizmatch.net',
|
||||
},
|
||||
ipinfo_token: '7029590fb91214',
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue