mirror of
https://github.com/Mueller-Patrick/Betterzon.git
synced 2024-11-21 22:03:58 +00:00
Merge pull request #26 from Mueller-Patrick/develop
Master Deployment Construction #1
This commit is contained in:
commit
aaf829f090
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -26,6 +26,7 @@ speed-measure-plugin*.json
|
|||
!Frontend.iml
|
||||
!Backend.iml
|
||||
!CucumberTests.iml
|
||||
!Crawler.iml
|
||||
|
||||
# Include IntelliJ modules
|
||||
!/.idea/modules.xml
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/Backend/Backend.iml" filepath="$PROJECT_DIR$/Backend/Backend.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/Betterzon.iml" filepath="$PROJECT_DIR$/Betterzon.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/Crawler/Crawler.iml" filepath="$PROJECT_DIR$/Crawler/Crawler.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/CucumberTests/CucumberTests.iml" filepath="$PROJECT_DIR$/CucumberTests/CucumberTests.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/Frontend/Frontend.iml" filepath="$PROJECT_DIR$/Frontend/Frontend.iml" />
|
||||
</modules>
|
||||
|
|
|
@ -19,7 +19,7 @@ export const categoriesRouter = express.Router();
|
|||
* Controller Definitions
|
||||
*/
|
||||
|
||||
// GET items/
|
||||
// GET categories/
|
||||
|
||||
categoriesRouter.get('/', async (req: Request, res: Response) => {
|
||||
try {
|
||||
|
@ -31,7 +31,7 @@ categoriesRouter.get('/', async (req: Request, res: Response) => {
|
|||
}
|
||||
});
|
||||
|
||||
// GET items/:id
|
||||
// GET categories/:id
|
||||
|
||||
categoriesRouter.get('/:id', async (req: Request, res: Response) => {
|
||||
const id: number = parseInt(req.params.id, 10);
|
||||
|
@ -50,7 +50,7 @@ categoriesRouter.get('/:id', async (req: Request, res: Response) => {
|
|||
}
|
||||
});
|
||||
|
||||
// GET items/:name
|
||||
// GET categories/search/:term
|
||||
|
||||
categoriesRouter.get('/search/:term', async (req: Request, res: Response) => {
|
||||
const term: string = req.params.term;
|
||||
|
|
|
@ -4,4 +4,7 @@ export interface Price {
|
|||
vendor_id: number;
|
||||
price_in_cents: number;
|
||||
timestamp: Date;
|
||||
// Only for deals
|
||||
amazonDifference?: number;
|
||||
amazonDifferencePercent?: number;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ export const pricesRouter = express.Router();
|
|||
* Controller Definitions
|
||||
*/
|
||||
|
||||
// GET items/
|
||||
// GET prices/
|
||||
|
||||
pricesRouter.get('/', async (req: Request, res: Response) => {
|
||||
try {
|
||||
|
@ -44,7 +44,7 @@ pricesRouter.get('/', async (req: Request, res: Response) => {
|
|||
}
|
||||
});
|
||||
|
||||
// GET items/:id
|
||||
// GET prices/:id
|
||||
|
||||
pricesRouter.get('/:id', async (req: Request, res: Response) => {
|
||||
const id: number = parseInt(req.params.id, 10);
|
||||
|
@ -63,6 +63,25 @@ pricesRouter.get('/:id', async (req: Request, res: Response) => {
|
|||
}
|
||||
});
|
||||
|
||||
// GET prices/bestDeals
|
||||
|
||||
pricesRouter.get('/bestDeals/:amount', async (req: Request, res: Response) => {
|
||||
const amount: number = parseInt(req.params.amount, 10);
|
||||
|
||||
if (!amount) {
|
||||
res.status(400).send('Missing parameters.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const prices: Prices = await PriceService.getBestDeals(amount);
|
||||
|
||||
res.status(200).send(prices);
|
||||
} catch (e) {
|
||||
res.status(404).send(e.message);
|
||||
}
|
||||
});
|
||||
|
||||
// POST items/
|
||||
|
||||
// pricesRouter.post('/', async (req: Request, res: Response) => {
|
||||
|
|
|
@ -186,6 +186,100 @@ export const findByVendor = async (product: string, vendor: string, type: string
|
|||
return priceRows;
|
||||
};
|
||||
|
||||
export const getBestDeals = async (amount: number): Promise<Prices> => {
|
||||
let conn;
|
||||
let priceRows = [];
|
||||
try {
|
||||
conn = await pool.getConnection();
|
||||
|
||||
let allPrices: Record<number, Price[]> = {};
|
||||
|
||||
// Get newest prices for every product at every vendor
|
||||
|
||||
const rows = await conn.query(
|
||||
'WITH summary AS (\n' +
|
||||
' SELECT p.product_id,\n' +
|
||||
' p.vendor_id,\n' +
|
||||
' p.price_in_cents,\n' +
|
||||
' p.timestamp,\n' +
|
||||
' ROW_NUMBER() OVER(\n' +
|
||||
' PARTITION BY p.product_id, p.vendor_id\n' +
|
||||
' ORDER BY p.timestamp DESC) AS rk\n' +
|
||||
' FROM prices p)\n' +
|
||||
'SELECT s.*\n' +
|
||||
'FROM summary s\n' +
|
||||
'WHERE s.rk = 1');
|
||||
|
||||
// Write returned values to allPrices map with product id as key and a list of prices as value
|
||||
for (let row in rows) {
|
||||
if (row !== 'meta') {
|
||||
if (!allPrices[parseInt(rows[row].product_id)]) {
|
||||
allPrices[parseInt(rows[row].product_id)] = [];
|
||||
}
|
||||
|
||||
allPrices[parseInt(rows[row].product_id)].push(rows[row]);
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over all prices to find the products with the biggest difference between amazon and other vendor
|
||||
let deals = [];
|
||||
for (let productId in Object.keys(allPrices)) {
|
||||
if (allPrices[productId]) {
|
||||
let pricesForProd = allPrices[productId];
|
||||
|
||||
// Get amazon price and lowest price from other vendor
|
||||
let amazonPrice = {} as Price;
|
||||
let lowestPrice = {} as Price;
|
||||
pricesForProd.forEach(function(price, priceIndex) {
|
||||
if (price.vendor_id === 1) {
|
||||
amazonPrice = price;
|
||||
} else {
|
||||
if (!lowestPrice.price_in_cents || lowestPrice.price_in_cents > price.price_in_cents) {
|
||||
lowestPrice = price;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Create deal object and add it to list
|
||||
let deal = {
|
||||
'product_id': lowestPrice.product_id,
|
||||
'vendor_id': lowestPrice.vendor_id,
|
||||
'price_in_cents': lowestPrice.price_in_cents,
|
||||
'timestamp' :lowestPrice.timestamp,
|
||||
'amazonDifference': (amazonPrice.price_in_cents - lowestPrice.price_in_cents),
|
||||
'amazonDifferencePercent': ((1 - (lowestPrice.price_in_cents / amazonPrice.price_in_cents)) * 100),
|
||||
};
|
||||
|
||||
// Push only deals were the amazon price is actually higher
|
||||
if(deal.amazonDifferencePercent > 0) {
|
||||
deals.push(deal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort to have the best deals on the top
|
||||
deals.sort((a, b) => a.amazonDifferencePercent < b.amazonDifferencePercent ? 1 : -1);
|
||||
|
||||
// Return only as many records as requested or the maximum amount of found deals, whatever is less
|
||||
let maxAmt = Math.min(amount, deals.length);
|
||||
|
||||
for (let dealIndex = 0; dealIndex < maxAmt; dealIndex++){
|
||||
//console.log(deals[dealIndex]);
|
||||
priceRows.push(deals[dealIndex] as Price);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
throw err;
|
||||
} finally {
|
||||
if (conn) {
|
||||
conn.end();
|
||||
}
|
||||
}
|
||||
|
||||
return priceRows;
|
||||
};
|
||||
|
||||
// export const create = async (newItem: Product): Promise<void> => {
|
||||
// let conn;
|
||||
// try {
|
||||
|
|
|
@ -19,7 +19,7 @@ export const productsRouter = express.Router();
|
|||
* Controller Definitions
|
||||
*/
|
||||
|
||||
// GET items/
|
||||
// GET products/
|
||||
|
||||
productsRouter.get('/', async (req: Request, res: Response) => {
|
||||
try {
|
||||
|
@ -31,7 +31,7 @@ productsRouter.get('/', async (req: Request, res: Response) => {
|
|||
}
|
||||
});
|
||||
|
||||
// GET items/:id
|
||||
// GET products/:id
|
||||
|
||||
productsRouter.get('/:id', async (req: Request, res: Response) => {
|
||||
const id: number = parseInt(req.params.id, 10);
|
||||
|
@ -50,7 +50,7 @@ productsRouter.get('/:id', async (req: Request, res: Response) => {
|
|||
}
|
||||
});
|
||||
|
||||
// GET items/:name
|
||||
// GET products/search/:term
|
||||
|
||||
productsRouter.get('/search/:term', async (req: Request, res: Response) => {
|
||||
const term: string = req.params.term;
|
||||
|
@ -69,6 +69,8 @@ productsRouter.get('/search/:term', async (req: Request, res: Response) => {
|
|||
}
|
||||
});
|
||||
|
||||
// GET products/bestDeals
|
||||
|
||||
|
||||
// POST items/
|
||||
|
||||
|
|
14
Crawler/Crawler.iml
Normal file
14
Crawler/Crawler.iml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="Python" name="Python">
|
||||
<configuration sdkName="Python 3.9 (venv)" />
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Python 3.9 (venv) interpreter library" level="application" />
|
||||
</component>
|
||||
</module>
|
20
Crawler/Dockerfile
Normal file
20
Crawler/Dockerfile
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Base image
|
||||
FROM python
|
||||
|
||||
# Create directories and copy files
|
||||
RUN echo 'Creating directory and copying files'
|
||||
RUN mkdir /crawler
|
||||
ADD . /crawler
|
||||
WORKDIR /crawler
|
||||
|
||||
# Install dependencies
|
||||
RUN echo 'Installing dependencies'
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
# Expose ports
|
||||
RUN echo 'Exposing ports'
|
||||
EXPOSE 22026
|
||||
|
||||
# Start API
|
||||
RUN echo 'Starting API'
|
||||
CMD ["python3", "api.py"]
|
16
Crawler/api.py
Normal file
16
Crawler/api.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
from flask import Flask
|
||||
from flask_restful import Resource, Api
|
||||
|
||||
app = Flask(__name__)
|
||||
api = Api(app)
|
||||
|
||||
|
||||
class CrawlerApi(Resource):
|
||||
def get(self):
|
||||
return {'Hallo': 'Betterzon'}
|
||||
|
||||
|
||||
api.add_resource(CrawlerApi, '/')
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=22026)
|
4
Crawler/requirements.txt
Normal file
4
Crawler/requirements.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
pymysql
|
||||
flask
|
||||
flask-sqlalchemy
|
||||
flask_restful
|
|
@ -24,9 +24,13 @@
|
|||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.css"
|
||||
"./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css",
|
||||
"src/styles.css",
|
||||
"./node_modules/cookieconsent/build/cookieconsent.min.css"
|
||||
],
|
||||
"scripts": []
|
||||
"scripts": [
|
||||
"./node_modules/cookieconsent/build/cookieconsent.min.js"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
|
@ -88,9 +92,13 @@
|
|||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.css"
|
||||
"./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css",
|
||||
"src/styles.css",
|
||||
"./node_modules/cookieconsent/build/cookieconsent.min.css"
|
||||
],
|
||||
"scripts": []
|
||||
"scripts": [
|
||||
"./node_modules/cookieconsent/build/cookieconsent.min.js"
|
||||
]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
|
|
43857
Frontend/package-lock.json
generated
43857
Frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -12,17 +12,21 @@
|
|||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^10.2.3",
|
||||
"@angular/cdk": "~10.2.7",
|
||||
"@angular/cli": "^10.2.0",
|
||||
"@angular/common": "^10.2.3",
|
||||
"@angular/compiler": "^10.2.3",
|
||||
"@angular/core": "^10.2.3",
|
||||
"@angular/forms": "^10.2.3",
|
||||
"@angular/material": "~10.2.7",
|
||||
"@angular/platform-browser": "^10.2.3",
|
||||
"@angular/platform-browser-dynamic": "^10.2.3",
|
||||
"@angular/router": "^10.2.3",
|
||||
"apexcharts": "^3.22.3",
|
||||
"cookieconsent": "^3.1.1",
|
||||
"ng": "0.0.0",
|
||||
"ng-apexcharts": "^1.5.6",
|
||||
"ngx-cookieconsent": "^2.2.3",
|
||||
"rxjs": "~6.6.0",
|
||||
"tslib": "^2.0.3",
|
||||
"zone.js": "~0.10.2"
|
||||
|
|
|
@ -1,10 +1,69 @@
|
|||
import { Component } from '@angular/core';
|
||||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {NgcCookieConsentService, NgcInitializeEvent, NgcNoCookieLawEvent, NgcStatusChangeEvent} from 'ngx-cookieconsent';
|
||||
import {Subscription} from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'Betterzon';
|
||||
export class AppComponent implements OnInit, OnDestroy {
|
||||
title = 'Betterzon';
|
||||
|
||||
// Cookie popup stuff
|
||||
// Docs on https://www.npmjs.com/package/ngx-cookieconsent
|
||||
private popupOpenSubscription: Subscription;
|
||||
private popupCloseSubscription: Subscription;
|
||||
private initializeSubscription: Subscription;
|
||||
private statusChangeSubscription: Subscription;
|
||||
private revokeChoiceSubscription: Subscription;
|
||||
private noCookieLawSubscription: Subscription;
|
||||
|
||||
constructor(
|
||||
private ccService: NgcCookieConsentService
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
// subscribe to cookieconsent observables to react to main events
|
||||
this.popupOpenSubscription = this.ccService.popupOpen$.subscribe(
|
||||
() => {
|
||||
// you can use this.ccService.getConfig() to do stuff...
|
||||
});
|
||||
|
||||
this.popupCloseSubscription = this.ccService.popupClose$.subscribe(
|
||||
() => {
|
||||
// you can use this.ccService.getConfig() to do stuff...
|
||||
});
|
||||
|
||||
this.initializeSubscription = this.ccService.initialize$.subscribe(
|
||||
(event: NgcInitializeEvent) => {
|
||||
// you can use this.ccService.getConfig() to do stuff...
|
||||
});
|
||||
|
||||
this.statusChangeSubscription = this.ccService.statusChange$.subscribe(
|
||||
(event: NgcStatusChangeEvent) => {
|
||||
// you can use this.ccService.getConfig() to do stuff...
|
||||
});
|
||||
|
||||
this.revokeChoiceSubscription = this.ccService.revokeChoice$.subscribe(
|
||||
() => {
|
||||
// you can use this.ccService.getConfig() to do stuff...
|
||||
});
|
||||
|
||||
this.noCookieLawSubscription = this.ccService.noCookieLaw$.subscribe(
|
||||
(event: NgcNoCookieLawEvent) => {
|
||||
// you can use this.ccService.getConfig() to do stuff...
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
// unsubscribe to cookieconsent observables to prevent memory leaks
|
||||
this.popupOpenSubscription.unsubscribe();
|
||||
this.popupCloseSubscription.unsubscribe();
|
||||
this.initializeSubscription.unsubscribe();
|
||||
this.statusChangeSubscription.unsubscribe();
|
||||
this.revokeChoiceSubscription.unsubscribe();
|
||||
this.noCookieLawSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,50 @@ import {HeaderComponent} from './components/header/header.component';
|
|||
import {NewestPricesListComponent} from './components/newest-prices-list/newest-prices-list.component';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {PageNotFoundPageComponent} from './pages/page-not-found-page/page-not-found-page.component';
|
||||
import {MatMenuModule} from '@angular/material/menu';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import {ImprintComponent} from './pages/imprint/imprint.component';
|
||||
import {PrivacyComponent} from './pages/privacy/privacy.component';
|
||||
import {NgcCookieConsentModule, NgcCookieConsentConfig} from 'ngx-cookieconsent';
|
||||
|
||||
// For cookie popup
|
||||
const cookieConfig: NgcCookieConsentConfig = {
|
||||
cookie: {
|
||||
domain: 'betterzon.xyz'
|
||||
},
|
||||
palette: {
|
||||
popup: {
|
||||
background: '#000'
|
||||
},
|
||||
button: {
|
||||
background: '#f1d600'
|
||||
}
|
||||
},
|
||||
theme: 'edgeless',
|
||||
type: 'opt-out',
|
||||
layout: 'my-custom-layout',
|
||||
layouts: {
|
||||
'my-custom-layout': '{{messagelink}}{{compliance}}'
|
||||
},
|
||||
elements: {
|
||||
messagelink: `
|
||||
<span id="cookieconsent:desc" class="cc-message">{{message}}
|
||||
<a aria-label="learn more about cookies" tabindex="0" class="cc-link" href="{{whatAreCookiesHref}}" target="_blank">{{whatAreCookiesLink}}</a>
|
||||
<a aria-label="learn more about our privacy policy" tabindex="1" class="cc-link" href="{{privacyPolicyHref}}" target="_blank">{{privacyPolicyLink}}</a>
|
||||
</span>
|
||||
`,
|
||||
},
|
||||
content: {
|
||||
// Custom message
|
||||
// message: 'By using our site, you acknowledge that you have read and understand our ',
|
||||
|
||||
whatAreCookiesLink: 'Learn more',
|
||||
whatAreCookiesHref: 'https://www.cookiesandyou.com/',
|
||||
|
||||
privacyPolicyLink: 'Privacy Policy',
|
||||
privacyPolicyHref: '/datenschutz',
|
||||
}
|
||||
};
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -27,14 +71,19 @@ import {PageNotFoundPageComponent} from './pages/page-not-found-page/page-not-fo
|
|||
ProductSearchPageComponent,
|
||||
HeaderComponent,
|
||||
NewestPricesListComponent,
|
||||
PageNotFoundPageComponent
|
||||
PageNotFoundPageComponent,
|
||||
ImprintComponent,
|
||||
PrivacyComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRouting,
|
||||
HttpClientModule,
|
||||
NgApexchartsModule,
|
||||
FormsModule
|
||||
FormsModule,
|
||||
MatMenuModule,
|
||||
BrowserAnimationsModule,
|
||||
NgcCookieConsentModule.forRoot(cookieConfig)
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
|
|
|
@ -7,11 +7,15 @@ import {LandingpageComponent} from './pages/landingpage/landingpage.component';
|
|||
import {ProductDetailPageComponent} from './pages/product-detail-page/product-detail-page.component';
|
||||
import {ProductSearchPageComponent} from './pages/product-search-page/product-search-page.component';
|
||||
import {PageNotFoundPageComponent} from './pages/page-not-found-page/page-not-found-page.component';
|
||||
import {ImprintComponent} from './pages/imprint/imprint.component';
|
||||
import {PrivacyComponent} from './pages/privacy/privacy.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{path: '', component: LandingpageComponent},
|
||||
{path: 'search', component: ProductSearchPageComponent},
|
||||
{path: 'product/:id', component: ProductDetailPageComponent},
|
||||
{path: 'impressum', component: ImprintComponent},
|
||||
{path: 'datenschutz', component: PrivacyComponent},
|
||||
{path: '**', component: PageNotFoundPageComponent}
|
||||
];
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
width: 100%;
|
||||
background-color: dimgrey;
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.icon-3d {
|
||||
|
@ -13,5 +12,23 @@
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
#footer-icons {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#imprintSection {
|
||||
position: fixed;
|
||||
right: 1em;
|
||||
bottom: 1em;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
#imprintSection a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -3,12 +3,16 @@
|
|||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
|
||||
<footer class="footer">
|
||||
<div class='wrap'>
|
||||
<div class='wrap' id="footer-icons">
|
||||
<div class="icons">
|
||||
<a href="https://github.com/Mueller-Patrick/Betterzon" class="fa fa-github fa-4x icon-3d"></a>
|
||||
<a href="https://twitter.com/elonmusk" class="fa fa-twitter fa-4x icon-3d"></a>
|
||||
<a href="https://www.facebook.com/TheElonmusk/" class="fa fa-facebook fa-4x icon-3d"></a>
|
||||
<a href="https://blog.betterzon.xyz/" class="fa fa-info fa-4x icon-3d"></a>
|
||||
<a href="https://github.com/Mueller-Patrick/Betterzon/wiki" class="fa fa-wikipedia-w fa-4x icon-3d"></a>
|
||||
</div>
|
||||
<div class = "blocks" id="copyright">© COPYRIGHT 2020 </div>
|
||||
<div class = "blocks" id="copyright">© COPYRIGHT 2020</div>
|
||||
</div>
|
||||
<div id="imprintSection">
|
||||
<a href="/impressum" >Imprint</a><br>
|
||||
<a href="/datenschutz">Privacy Policy</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-footer',
|
||||
|
@ -7,9 +8,16 @@ import { Component, OnInit } from '@angular/core';
|
|||
})
|
||||
export class FooterComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
constructor(
|
||||
private router: Router,
|
||||
private route: ActivatedRoute
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
navigateImprint(): void {
|
||||
this.router.navigate([('/impressum/')]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
margin-left: 50%;
|
||||
}
|
||||
|
||||
.logo:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.searchBox {
|
||||
position: relative;
|
||||
margin: auto;
|
||||
|
@ -36,6 +40,10 @@
|
|||
margin-left: 10%;
|
||||
}
|
||||
|
||||
.profileIcon:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon-3d {
|
||||
padding: 10px;
|
||||
color: #fff;
|
||||
|
|
|
@ -8,7 +8,13 @@
|
|||
<input *ngIf="showSearch===true" type="text" [(ngModel)]="searchInput" placeholder="Search" (keyup.enter)="startedSearch()">
|
||||
</div>
|
||||
<div class="profileIcon">
|
||||
Profile
|
||||
<button mat-button [matMenuTriggerFor]="menu">Menu</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item>Option 1</button>
|
||||
<button mat-menu-item>Option 2</button>
|
||||
<button mat-menu-item>Option 3</button>
|
||||
<button mat-menu-item>Option 4</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
@ -4,8 +4,9 @@ import {Router} from '@angular/router';
|
|||
@Component({
|
||||
selector: 'app-header',
|
||||
templateUrl: './header.component.html',
|
||||
styleUrls: ['./header.component.css']
|
||||
styleUrls: ['./header.component.css'],
|
||||
})
|
||||
|
||||
export class HeaderComponent implements OnInit {
|
||||
searchInput: string;
|
||||
@Input() showSearch: boolean;
|
||||
|
|
|
@ -81,3 +81,8 @@
|
|||
margin-top: .5em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.priceAlarm:hover {
|
||||
background-color: lightgray;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
@ -53,3 +53,8 @@
|
|||
.productDescription {
|
||||
grid-area: description;
|
||||
}
|
||||
|
||||
.productItem:hover {
|
||||
background-color: lightgray;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
4
Frontend/src/app/pages/imprint/imprint.component.css
Normal file
4
Frontend/src/app/pages/imprint/imprint.component.css
Normal file
|
@ -0,0 +1,4 @@
|
|||
#imprint {
|
||||
padding-left: 5em;
|
||||
padding-right: 5em;
|
||||
}
|
59
Frontend/src/app/pages/imprint/imprint.component.html
Normal file
59
Frontend/src/app/pages/imprint/imprint.component.html
Normal file
|
@ -0,0 +1,59 @@
|
|||
<app-header [showSearch]="true"></app-header>
|
||||
<div id="imprint">
|
||||
<h1>Impressum</h1>
|
||||
|
||||
<h2>Angaben gemäß § 5 TMG</h2>
|
||||
<p>
|
||||
Patrick Müller<br/>
|
||||
Schachtstraße 8<br/>
|
||||
75031 Eppingen
|
||||
</p>
|
||||
|
||||
<h2>Kontakt</h2>
|
||||
<p>
|
||||
E-Mail: betterzon-contact@mueller-patrick.tech
|
||||
</p>
|
||||
|
||||
<h3>Haftung für Inhalte</h3>
|
||||
<p>
|
||||
Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene
|
||||
Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als
|
||||
Diensteanbieter jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen
|
||||
oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen.
|
||||
</p>
|
||||
<p>
|
||||
Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben
|
||||
hiervon unberührt.
|
||||
Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich.
|
||||
Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen.
|
||||
</p>
|
||||
|
||||
<h3>Haftung für Links</h3>
|
||||
<p>
|
||||
Unser Angebot enthält Links zu externen Websites Dritter, auf deren Inhalte wir keinen
|
||||
Einfluss haben. Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für
|
||||
die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich. Die
|
||||
verlinkten Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße überprüft.
|
||||
Rechtswidrige Inhalte waren zum Zeitpunkt der Verlinkung nicht erkennbar.
|
||||
</p>
|
||||
<p>
|
||||
Eine permanente inhaltliche Kontrolle der verlinkten Seiten ist jedoch ohne konkrete Anhaltspunkte einer
|
||||
Rechtsverletzung nicht zumutbar. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Links umgehend
|
||||
entfernen.
|
||||
</p>
|
||||
|
||||
<h3>Urheberrecht</h3>
|
||||
<p>
|
||||
Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem deutschen Urheberrecht.
|
||||
Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der Grenzen des
|
||||
Urheberrechtes bedürfen der schriftlichen Zustimmung des jeweiligen Autors bzw. Erstellers. Downloads und
|
||||
Kopien dieser Seite sind nur für den privaten, nicht kommerziellen Gebrauch gestattet.
|
||||
</p>
|
||||
<p>
|
||||
Soweit die Inhalte auf dieser Seite nicht vom Betreiber erstellt wurden, werden die Urheberrechte Dritter beachtet.
|
||||
Insbesondere werden Inhalte Dritter als solche gekennzeichnet. Sollten Sie trotzdem auf eine Urheberrechtsverletzung
|
||||
aufmerksam werden, bitten wir um einen entsprechenden Hinweis. Bei Bekanntwerden von Rechtsverletzungen werden wir
|
||||
derartige Inhalte umgehend entfernen.
|
||||
</p>
|
||||
</div>
|
||||
<app-footer></app-footer>
|
25
Frontend/src/app/pages/imprint/imprint.component.spec.ts
Normal file
25
Frontend/src/app/pages/imprint/imprint.component.spec.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ImprintComponent } from './imprint.component';
|
||||
|
||||
describe('ImprintComponent', () => {
|
||||
let component: ImprintComponent;
|
||||
let fixture: ComponentFixture<ImprintComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ ImprintComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ImprintComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
15
Frontend/src/app/pages/imprint/imprint.component.ts
Normal file
15
Frontend/src/app/pages/imprint/imprint.component.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-imprint',
|
||||
templateUrl: './imprint.component.html',
|
||||
styleUrls: ['./imprint.component.css']
|
||||
})
|
||||
export class ImprintComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
4
Frontend/src/app/pages/privacy/privacy.component.css
Normal file
4
Frontend/src/app/pages/privacy/privacy.component.css
Normal file
|
@ -0,0 +1,4 @@
|
|||
#privacy {
|
||||
padding-left: 5em;
|
||||
padding-right: 5em;
|
||||
}
|
408
Frontend/src/app/pages/privacy/privacy.component.html
Normal file
408
Frontend/src/app/pages/privacy/privacy.component.html
Normal file
|
@ -0,0 +1,408 @@
|
|||
<app-header [showSearch]="true"></app-header>
|
||||
<div id="privacy">
|
||||
<h1>Datenschutz­erklärung</h1>
|
||||
<h2>1. Datenschutz auf einen Blick</h2>
|
||||
<h3>Allgemeine Hinweise</h3>
|
||||
<p>
|
||||
Die folgenden Hinweise geben einen einfachen Überblick darüber, was mit Ihren
|
||||
personenbezogenen Daten passiert, wenn Sie diese Website besuchen. Personenbezogene Daten sind alle Daten, mit denen
|
||||
Sie persönlich identifiziert werden können. Ausführliche Informationen zum Thema Datenschutz
|
||||
entnehmen Sie unserer unter diesem Text aufgeführten Datenschutzerklärung.
|
||||
</p>
|
||||
|
||||
<h3>Datenerfassung auf dieser Website</h3>
|
||||
<h4>Wer ist verantwortlich für die Datenerfassung auf dieser Website?</h4>
|
||||
<p>
|
||||
Die Datenverarbeitung auf dieser Website erfolgt durch den Websitebetreiber. Dessen Kontaktdaten können
|
||||
Sie dem Abschnitt „Hinweis zur Verantwortlichen Stelle“ in dieser Datenschutzerklärung
|
||||
entnehmen.
|
||||
</p>
|
||||
|
||||
<h4>Wie erfassen wir Ihre Daten?</h4>
|
||||
<p>
|
||||
Ihre Daten werden zum einen dadurch erhoben, dass Sie uns
|
||||
diese mitteilen. Hierbei kann es sich z. B. um Daten handeln, die Sie in ein Kontaktformular eingeben.
|
||||
</p>
|
||||
<p>
|
||||
Andere Daten werden automatisch oder nach Ihrer Einwilligung beim Besuch der Website durch unsere IT-Systeme
|
||||
erfasst. Das sind vor allem technische Daten (z. B. Internetbrowser, Betriebssystem oder Uhrzeit des
|
||||
Seitenaufrufs). Die Erfassung dieser Daten erfolgt automatisch, sobald Sie diese Website betreten.
|
||||
</p>
|
||||
|
||||
<h4>Wofür nutzen wir Ihre Daten?</h4>
|
||||
<p>
|
||||
Ein Teil der Daten wird erhoben, um eine fehlerfreie Bereitstellung der Website zu
|
||||
gewährleisten. Andere Daten können zur Analyse Ihres Nutzerverhaltens verwendet werden.
|
||||
</p>
|
||||
|
||||
<h4>Welche Rechte haben Sie bezüglich Ihrer Daten?</h4>
|
||||
<p>
|
||||
Sie haben jederzeit das Recht, unentgeltlich Auskunft über
|
||||
Herkunft, Empfänger und Zweck Ihrer gespeicherten personenbezogenen Daten zu erhalten. Sie haben außerdem
|
||||
ein Recht, die Berichtigung oder Löschung dieser Daten zu verlangen. Wenn Sie eine Einwilligung zur
|
||||
Datenverarbeitung erteilt haben, können Sie diese Einwilligung jederzeit für die Zukunft widerrufen. Außerdem
|
||||
haben Sie das Recht, unter bestimmten Umständen die Einschränkung der Verarbeitung Ihrer personenbezogenen
|
||||
Daten zu verlangen. Des Weiteren steht Ihnen ein Beschwerderecht bei der zuständigen Aufsichtsbehörde
|
||||
zu.
|
||||
</p>
|
||||
<p>
|
||||
Hierzu sowie zu weiteren Fragen zum Thema Datenschutz können Sie sich jederzeit an uns wenden.
|
||||
</p>
|
||||
|
||||
<h2>2. Hosting und Content Delivery Networks (CDN)</h2>
|
||||
<h3>Externes Hosting</h3>
|
||||
<p>
|
||||
Diese Website wird bei einem externen Dienstleister gehostet (Hoster). Die
|
||||
personenbezogenen Daten, die auf dieser Website erfasst werden, werden auf den Servern des Hosters gespeichert.
|
||||
Hierbei kann es sich v. a. um IP-Adressen, Kontaktanfragen, Meta- und Kommunikationsdaten, Vertragsdaten,
|
||||
Kontaktdaten, Namen, Websitezugriffe und sonstige Daten, die über eine Website generiert werden, handeln.
|
||||
</p>
|
||||
<p>
|
||||
Der Einsatz des Hosters erfolgt zum Zwecke der Vertragserfüllung gegenüber unseren potenziellen und
|
||||
bestehenden Kunden (Art. 6 Abs. 1 lit. b DSGVO) und im Interesse einer sicheren, schnellen und effizienten
|
||||
Bereitstellung unseres Online-Angebots durch einen professionellen Anbieter (Art. 6 Abs. 1 lit. f DSGVO).
|
||||
</p>
|
||||
<p>
|
||||
Unser Hoster wird Ihre Daten nur insoweit verarbeiten, wie dies zur Erfüllung seiner Leistungspflichten
|
||||
erforderlich ist und unsere Weisungen in Bezug auf diese Daten befolgen.
|
||||
</p>
|
||||
<p>
|
||||
Wir setzen folgenden Hoster ein:
|
||||
</p>
|
||||
<p>
|
||||
netcup GmbH<br/>
|
||||
Daimlerstraße 25<br/>
|
||||
76185 Karlsruhe
|
||||
</p>
|
||||
|
||||
<h4>Abschluss eines Vertrages über Auftragsverarbeitung</h4>
|
||||
<p>
|
||||
Um die datenschutzkonforme Verarbeitung zu gewährleisten,
|
||||
haben wir einen Vertrag über Auftragsverarbeitung mit unserem Hoster geschlossen.
|
||||
</p>
|
||||
|
||||
<h2>3. Allgemeine Hinweise und Pflicht­informationen</h2>
|
||||
<h3>Datenschutz</h3>
|
||||
<p>
|
||||
Die Betreiber dieser Seiten nehmen den Schutz Ihrer persönlichen Daten sehr ernst. Wir
|
||||
behandeln Ihre personenbezogenen Daten vertraulich und entsprechend der gesetzlichen Datenschutzvorschriften sowie
|
||||
dieser Datenschutzerklärung.
|
||||
</p>
|
||||
<p>
|
||||
Wenn Sie diese Website benutzen, werden verschiedene personenbezogene Daten
|
||||
erhoben. Personenbezogene Daten sind Daten, mit denen Sie persönlich identifiziert werden können. Die
|
||||
vorliegende Datenschutzerklärung erläutert, welche Daten wir erheben und wofür wir sie nutzen. Sie
|
||||
erläutert auch, wie und zu welchem Zweck das geschieht.
|
||||
</p>
|
||||
<p>
|
||||
Wir weisen darauf hin, dass die Datenübertragung
|
||||
im Internet (z. B. bei der Kommunikation per E-Mail) Sicherheitslücken aufweisen kann. Ein lückenloser
|
||||
Schutz der Daten vor dem Zugriff durch Dritte ist nicht möglich.
|
||||
</p>
|
||||
|
||||
<h3>Hinweis zur verantwortlichen Stelle</h3>
|
||||
<p>
|
||||
Die verantwortliche Stelle für die Datenverarbeitung auf dieser
|
||||
Website ist:
|
||||
</p>
|
||||
<p>
|
||||
Patrick Müller<br/>
|
||||
Schachtstraße 8<br/>
|
||||
75031 Eppingen
|
||||
</p>
|
||||
|
||||
<p>
|
||||
E-Mail: betterzon-privacy@mueller-patrick.tech
|
||||
</p>
|
||||
<p>
|
||||
Verantwortliche Stelle ist die natürliche oder juristische Person, die allein oder gemeinsam mit anderen über
|
||||
die Zwecke und Mittel der Verarbeitung von personenbezogenen Daten (z. B. Namen, E-Mail-Adressen o. Ä.)
|
||||
entscheidet.
|
||||
</p>
|
||||
|
||||
<h3>Speicherdauer</h3>
|
||||
<p>
|
||||
Soweit innerhalb dieser Datenschutzerklärung keine speziellere Speicherdauer genannt
|
||||
wurde, verbleiben Ihre personenbezogenen Daten bei uns, bis der Zweck für die Datenverarbeitung entfällt.
|
||||
Wenn Sie ein berechtigtes Löschersuchen geltend machen oder eine Einwilligung zur Datenverarbeitung widerrufen,
|
||||
werden Ihre Daten gelöscht, sofern wir keine anderen rechtlich zulässigen Gründe für die
|
||||
Speicherung Ihrer personenbezogenen Daten haben (z.B. steuer- oder handelsrechtliche Aufbewahrungsfristen); im
|
||||
letztgenannten Fall erfolgt die Löschung nach Fortfall dieser Gründe.
|
||||
</p>
|
||||
|
||||
<h3>Hinweis zur Datenweitergabe in die USA und sonstige Drittstaaten</h3>
|
||||
<p>
|
||||
Auf unserer Website sind unter anderem
|
||||
Tools von Unternehmen mit Sitz in den USA oder sonstigen datenschutzrechtlich nicht sicheren Drittstaaten
|
||||
eingebunden. Wenn diese Tools aktiv sind, können Ihre personenbezogene Daten in diese Drittstaaten übertragen
|
||||
und dort verarbeitet werden. Wir weisen darauf hin, dass in diesen Ländern kein mit der EU vergleichbares
|
||||
Datenschutzniveau garantiert werden kann. Beispielsweise sind US-Unternehmen dazu verpflichtet, personenbezogene
|
||||
Daten an Sicherheitsbehörden herauszugeben, ohne dass Sie als Betroffener hiergegen gerichtlich vorgehen könnten.
|
||||
Es kann daher nicht ausgeschlossen werden, dass US-Behörden (z.B. Geheimdienste) Ihre auf US-Servern
|
||||
befindlichen Daten zu Überwachungszwecken verarbeiten, auswerten und dauerhaft speichern. Wir haben auf diese
|
||||
Verarbeitungstätigkeiten keinen Einfluss.
|
||||
</p>
|
||||
|
||||
<h3>Widerruf Ihrer Einwilligung zur Datenverarbeitung</h3>
|
||||
<p>
|
||||
Viele Datenverarbeitungsvorgänge sind nur mit Ihrer ausdrücklichen Einwilligung möglich. Sie können
|
||||
eine bereits erteilte Einwilligung jederzeit widerrufen. Die Rechtmäßigkeit der bis zum Widerruf
|
||||
erfolgten Datenverarbeitung bleibt vom Widerruf unberührt.
|
||||
</p>
|
||||
|
||||
<h3>Widerspruchsrecht gegen die Datenerhebung in besonderen Fällen sowie gegen Direktwerbung (Art. 21 DSGVO)</h3>
|
||||
<p>
|
||||
WENN DIE DATENVERARBEITUNG AUF GRUNDLAGE VON ART. 6 ABS. 1 LIT. E ODER F DSGVO ERFOLGT, HABEN SIE JEDERZEIT DAS
|
||||
RECHT, AUS GRÜNDEN, DIE SICH AUS IHRER BESONDEREN SITUATION ERGEBEN, GEGEN DIE VERARBEITUNG IHRER
|
||||
PERSONENBEZOGENEN DATEN WIDERSPRUCH EINZULEGEN; DIES GILT AUCH FÜR EIN AUF DIESE BESTIMMUNGEN GESTÜTZTES
|
||||
PROFILING. DIE JEWEILIGE RECHTSGRUNDLAGE, AUF DENEN EINE VERARBEITUNG BERUHT, ENTNEHMEN SIE DIESER DATENSCHUTZERKLÄRUNG.
|
||||
WENN SIE WIDERSPRUCH EINLEGEN, WERDEN WIR IHRE BETROFFENEN PERSONENBEZOGENEN DATEN NICHT MEHR VERARBEITEN, ES SEI
|
||||
DENN, WIR KÖNNEN ZWINGENDE SCHUTZWÜRDIGE GRÜNDE FÜR DIE VERARBEITUNG NACHWEISEN, DIE IHRE
|
||||
INTERESSEN, RECHTE UND FREIHEITEN ÜBERWIEGEN ODER DIE VERARBEITUNG DIENT DER GELTENDMACHUNG, AUSÜBUNG ODER
|
||||
VERTEIDIGUNG VON RECHTSANSPRÜCHEN (WIDERSPRUCH NACH ART. 21 ABS. 1 DSGVO).
|
||||
</p>
|
||||
<p>
|
||||
WERDEN IHRE PERSONENBEZOGENEN
|
||||
DATEN VERARBEITET, UM DIREKTWERBUNG ZU BETREIBEN, SO HABEN SIE DAS RECHT, JEDERZEIT WIDERSPRUCH GEGEN DIE
|
||||
VERARBEITUNG SIE BETREFFENDER PERSONENBEZOGENER DATEN ZUM ZWECKE DERARTIGER WERBUNG EINZULEGEN; DIES GILT AUCH FÜR
|
||||
DAS PROFILING, SOWEIT ES MIT SOLCHER DIREKTWERBUNG IN VERBINDUNG STEHT. WENN SIE WIDERSPRECHEN, WERDEN IHRE
|
||||
PERSONENBEZOGENEN DATEN ANSCHLIESSEND NICHT MEHR ZUM ZWECKE DER DIREKTWERBUNG VERWENDET (WIDERSPRUCH NACH ART. 21
|
||||
ABS. 2 DSGVO).
|
||||
</p>
|
||||
|
||||
<h3>Beschwerde­recht bei der zuständigen Aufsichts­behörde</h3>
|
||||
<p>
|
||||
Im Falle von Verstößen
|
||||
gegen die DSGVO steht den Betroffenen ein Beschwerderecht bei einer Aufsichtsbehörde, insbesondere in dem
|
||||
Mitgliedstaat ihres gewöhnlichen Aufenthalts, ihres Arbeitsplatzes oder des Orts des mutmaßlichen Verstoßes
|
||||
zu. Das Beschwerderecht besteht unbeschadet anderweitiger verwaltungsrechtlicher oder gerichtlicher
|
||||
Rechtsbehelfe.
|
||||
</p>
|
||||
|
||||
<h3>Recht auf Daten­übertrag­barkeit</h3>
|
||||
<p>
|
||||
Sie haben das Recht, Daten, die wir auf Grundlage Ihrer
|
||||
Einwilligung oder in Erfüllung eines Vertrags automatisiert verarbeiten, an sich oder an einen Dritten in einem
|
||||
gängigen, maschinenlesbaren Format aushändigen zu lassen. Sofern Sie die direkte Übertragung der
|
||||
Daten an einen anderen Verantwortlichen verlangen, erfolgt dies nur, soweit es technisch machbar ist.
|
||||
</p>
|
||||
|
||||
<h3>SSL- bzw. TLS-Verschlüsselung</h3>
|
||||
<p>
|
||||
Diese Seite nutzt aus Sicherheitsgründen und zum Schutz der Übertragung
|
||||
vertraulicher Inhalte, wie zum Beispiel Bestellungen oder Anfragen, die Sie an uns als Seitenbetreiber senden, eine
|
||||
SSL- bzw. TLS-Verschlüsselung. Eine verschlüsselte Verbindung erkennen Sie daran, dass die Adresszeile des
|
||||
Browsers von „http://“ auf „https://“ wechselt und an dem Schloss-Symbol in Ihrer
|
||||
Browserzeile.
|
||||
</p>
|
||||
<p>
|
||||
Wenn die SSL- bzw. TLS-Verschlüsselung aktiviert ist, können die Daten, die Sie an
|
||||
uns übermitteln, nicht von Dritten mitgelesen werden.
|
||||
</p>
|
||||
|
||||
<h3>Auskunft, Löschung und Berichtigung</h3>
|
||||
<p>
|
||||
Sie haben im Rahmen der geltenden gesetzlichen Bestimmungen
|
||||
jederzeit das Recht auf unentgeltliche Auskunft über Ihre gespeicherten personenbezogenen Daten, deren Herkunft
|
||||
und Empfänger und den Zweck der Datenverarbeitung und ggf. ein Recht auf Berichtigung oder Löschung dieser
|
||||
Daten. Hierzu sowie zu weiteren Fragen zum Thema personenbezogene Daten können Sie sich jederzeit an uns
|
||||
wenden.
|
||||
</p>
|
||||
<h3>Recht auf Einschränkung der Verarbeitung</h3>
|
||||
<p>
|
||||
Sie haben das Recht, die Einschränkung der Verarbeitung
|
||||
Ihrer personenbezogenen Daten zu verlangen. Hierzu können Sie sich jederzeit an uns wenden. Das Recht auf
|
||||
Einschränkung der Verarbeitung besteht in folgenden Fällen:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
Wenn Sie die Richtigkeit Ihrer bei uns gespeicherten personenbezogenen Daten bestreiten, benötigen wir in
|
||||
der Regel Zeit, um dies zu überprüfen. Für die Dauer der Prüfung haben Sie das Recht, die
|
||||
Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.
|
||||
</li>
|
||||
<li>
|
||||
Wenn die Verarbeitung Ihrer personenbezogenen Daten unrechtmäßig geschah/geschieht, können Sie
|
||||
statt der Löschung die Einschränkung der Datenverarbeitung verlangen.
|
||||
</li>
|
||||
<li>
|
||||
Wenn wir Ihre personenbezogenen Daten nicht mehr benötigen, Sie sie jedoch zur Ausübung, Verteidigung
|
||||
oder Geltendmachung von Rechtsansprüchen benötigen, haben Sie das Recht, statt der Löschung die
|
||||
Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.
|
||||
</li>
|
||||
<li>
|
||||
Wenn Sie einen Widerspruch nach Art. 21 Abs. 1 DSGVO eingelegt haben, muss eine Abwägung zwischen Ihren und
|
||||
unseren Interessen vorgenommen werden. Solange noch nicht feststeht, wessen Interessen überwiegen, haben
|
||||
Sie das Recht, die Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
Wenn Sie die Verarbeitung Ihrer personenbezogenen Daten eingeschränkt haben, dürfen diese Daten
|
||||
– von ihrer Speicherung abgesehen – nur mit Ihrer Einwilligung oder zur Geltendmachung, Ausübung
|
||||
oder Verteidigung von Rechtsansprüchen oder zum Schutz der Rechte einer anderen natürlichen oder
|
||||
juristischen Person oder aus Gründen eines wichtigen öffentlichen Interesses der Europäischen Union
|
||||
oder eines Mitgliedstaats verarbeitet werden.
|
||||
</p>
|
||||
|
||||
<h2>4. Datenerfassung auf dieser Website</h2>
|
||||
<h3>Cookies</h3>
|
||||
<p>
|
||||
Unsere Internetseiten verwenden so genannte „Cookies“. Cookies sind kleine Textdateien
|
||||
und richten auf Ihrem Endgerät keinen Schaden an. Sie werden entweder vorübergehend für die Dauer
|
||||
einer Sitzung (Session-Cookies) oder dauerhaft (permanente Cookies) auf Ihrem Endgerät gespeichert.
|
||||
Session-Cookies werden nach Ende Ihres Besuchs automatisch gelöscht. Permanente Cookies bleiben auf Ihrem
|
||||
Endgerät gespeichert, bis Sie diese selbst löschen oder eine automatische Löschung durch Ihren
|
||||
Webbrowser erfolgt.
|
||||
</p>
|
||||
<p>
|
||||
Teilweise können auch Cookies von Drittunternehmen auf Ihrem Endgerät
|
||||
gespeichert werden, wenn Sie unsere Seite betreten (Third-Party-Cookies). Diese ermöglichen uns oder Ihnen die
|
||||
Nutzung bestimmter Dienstleistungen des Drittunternehmens (z.B. Cookies zur Abwicklung von
|
||||
Zahlungsdienstleistungen).
|
||||
</p>
|
||||
<p>
|
||||
Cookies haben verschiedene Funktionen. Zahlreiche Cookies sind technisch
|
||||
notwendig, da bestimmte Websitefunktionen ohne diese nicht funktionieren würden (z.B. die Warenkorbfunktion
|
||||
oder die Anzeige von Videos). Andere Cookies dienen dazu, das Nutzerverhalten auszuwerten oder Werbung
|
||||
anzuzeigen.
|
||||
</p>
|
||||
<p>
|
||||
Cookies, die zur Durchführung des elektronischen Kommunikationsvorgangs (notwendige Cookies)
|
||||
oder zur Bereitstellung bestimmter, von Ihnen erwünschter Funktionen (funktionale Cookies, z. B. für die
|
||||
Warenkorbfunktion) oder zur Optimierung der Website (z.B. Cookies zur Messung des Webpublikums) erforderlich sind,
|
||||
werden auf Grundlage von Art. 6 Abs. 1 lit. f DSGVO gespeichert, sofern keine andere Rechtsgrundlage angegeben wird.
|
||||
Der Websitebetreiber hat ein berechtigtes Interesse an der Speicherung von Cookies zur technisch fehlerfreien und
|
||||
optimierten Bereitstellung seiner Dienste. Sofern eine Einwilligung zur Speicherung von Cookies abgefragt wurde,
|
||||
erfolgt die Speicherung der betreffenden Cookies ausschließlich auf Grundlage dieser Einwilligung (Art. 6 Abs.
|
||||
1 lit. a DSGVO); die Einwilligung ist jederzeit widerrufbar.
|
||||
</p>
|
||||
<p>
|
||||
Sie können Ihren Browser so einstellen,
|
||||
dass Sie über das Setzen von Cookies informiert werden und Cookies nur im Einzelfall erlauben, die Annahme von
|
||||
Cookies für bestimmte Fälle oder generell ausschließen sowie das automatische Löschen der
|
||||
Cookies beim Schließen des Browsers aktivieren. Bei der Deaktivierung von Cookies kann die Funktionalität
|
||||
dieser Website eingeschränkt sein.
|
||||
</p>
|
||||
<p>
|
||||
Soweit Cookies von Drittunternehmen oder zu Analysezwecken eingesetzt
|
||||
werden, werden wir Sie hierüber im Rahmen dieser Datenschutzerklärung gesondert informieren und ggf. eine
|
||||
Einwilligung abfragen.
|
||||
</p>
|
||||
|
||||
<h3>Server-Log-Dateien</h3>
|
||||
<p>
|
||||
Der Provider der Seiten erhebt und speichert automatisch Informationen in so genannten
|
||||
Server-Log-Dateien, die Ihr Browser automatisch an uns übermittelt. Dies sind:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Browsertyp und Browserversion</li>
|
||||
<li>verwendetes Betriebssystem</li>
|
||||
<li>Referrer URL</li>
|
||||
<li>Hostname des zugreifenden Rechners</li>
|
||||
<li>Uhrzeit der Serveranfrage</li>
|
||||
<li>IP-Adresse</li>
|
||||
</ul>
|
||||
<p>
|
||||
Eine Zusammenführung dieser Daten mit anderen Datenquellen wird nicht vorgenommen.
|
||||
</p>
|
||||
<p>
|
||||
Die Erfassung
|
||||
dieser Daten erfolgt auf Grundlage von Art. 6 Abs. 1 lit. f DSGVO. Der Websitebetreiber hat ein berechtigtes
|
||||
Interesse an der technisch fehlerfreien Darstellung und der Optimierung seiner Website – hierzu müssen
|
||||
die Server-Log-Files erfasst werden.
|
||||
</p>
|
||||
|
||||
<h3>Kontaktformular</h3>
|
||||
<p>
|
||||
Wenn Sie uns per Kontaktformular Anfragen zukommen lassen, werden Ihre Angaben aus dem
|
||||
Anfrageformular inklusive der von Ihnen dort angegebenen Kontaktdaten zwecks Bearbeitung der Anfrage und für
|
||||
den Fall von Anschlussfragen bei uns gespeichert. Diese Daten geben wir nicht ohne Ihre Einwilligung weiter.
|
||||
</p>
|
||||
<p>
|
||||
Die Verarbeitung dieser Daten erfolgt auf Grundlage von Art. 6 Abs. 1 lit. b DSGVO, sofern Ihre Anfrage mit der Erfüllung
|
||||
eines Vertrags zusammenhängt oder zur Durchführung vorvertraglicher Maßnahmen erforderlich ist. In
|
||||
allen übrigen Fällen beruht die Verarbeitung auf unserem berechtigten Interesse an der effektiven
|
||||
Bearbeitung der an uns gerichteten Anfragen (Art. 6 Abs. 1 lit. f DSGVO) oder auf Ihrer Einwilligung (Art. 6 Abs. 1
|
||||
lit. a DSGVO) sofern diese abgefragt wurde.
|
||||
</p>
|
||||
<p>
|
||||
Die von Ihnen im Kontaktformular eingegebenen Daten verbleiben
|
||||
bei uns, bis Sie uns zur Löschung auffordern, Ihre Einwilligung zur Speicherung widerrufen oder der Zweck für
|
||||
die Datenspeicherung entfällt (z. B. nach abgeschlossener Bearbeitung Ihrer Anfrage). Zwingende
|
||||
gesetzliche Bestimmungen – insbesondere Aufbewahrungsfristen – bleiben unberührt.
|
||||
</p>
|
||||
|
||||
<h3>Anfrage per E-Mail, Telefon oder Telefax</h3>
|
||||
<p>
|
||||
Wenn Sie uns per E-Mail, Telefon oder Telefax kontaktieren, wird
|
||||
Ihre Anfrage inklusive aller daraus hervorgehenden personenbezogenen Daten (Name, Anfrage) zum Zwecke der
|
||||
Bearbeitung Ihres Anliegens bei uns gespeichert und verarbeitet. Diese Daten geben wir nicht ohne Ihre Einwilligung
|
||||
weiter.
|
||||
</p>
|
||||
<p>
|
||||
Die Verarbeitung dieser Daten erfolgt auf Grundlage von Art. 6 Abs. 1 lit. b DSGVO, sofern Ihre
|
||||
Anfrage mit der Erfüllung eines Vertrags zusammenhängt oder zur Durchführung vorvertraglicher Maßnahmen
|
||||
erforderlich ist. In allen übrigen Fällen beruht die Verarbeitung auf unserem berechtigten Interesse an
|
||||
der effektiven Bearbeitung der an uns gerichteten Anfragen (Art. 6 Abs. 1 lit. f DSGVO) oder auf Ihrer Einwilligung
|
||||
(Art. 6 Abs. 1 lit. a DSGVO) sofern diese abgefragt wurde.
|
||||
</p>
|
||||
<p>
|
||||
Die von Ihnen an uns per Kontaktanfragen übersandten
|
||||
Daten verbleiben bei uns, bis Sie uns zur Löschung auffordern, Ihre Einwilligung zur Speicherung widerrufen
|
||||
oder der Zweck für die Datenspeicherung entfällt (z. B. nach abgeschlossener Bearbeitung Ihres
|
||||
Anliegens). Zwingende gesetzliche Bestimmungen – insbesondere gesetzliche Aufbewahrungsfristen – bleiben
|
||||
unberührt.
|
||||
</p>
|
||||
|
||||
<h2>5. Plugins und Tools</h2>
|
||||
<h3>Google Web Fonts</h3>
|
||||
<p>
|
||||
Diese Seite nutzt zur einheitlichen Darstellung von Schriftarten so genannte Web Fonts, die
|
||||
von Google bereitgestellt werden. Beim Aufruf einer Seite lädt Ihr Browser die benötigten Web Fonts in
|
||||
ihren Browsercache, um Texte und Schriftarten korrekt anzuzeigen.
|
||||
</p>
|
||||
<p>
|
||||
Zu diesem Zweck muss der von Ihnen
|
||||
verwendete Browser Verbindung zu den Servern von Google aufnehmen. Hierdurch erlangt Google Kenntnis darüber,
|
||||
dass über Ihre IP-Adresse diese Website aufgerufen wurde. Die Nutzung von Google WebFonts erfolgt auf Grundlage
|
||||
von Art. 6 Abs. 1 lit. f DSGVO. Der Websitebetreiber hat ein berechtigtes Interesse an der einheitlichen Darstellung
|
||||
des Schriftbildes auf seiner Website. Sofern eine entsprechende Einwilligung abgefragt wurde (z. B. eine
|
||||
Einwilligung zur Speicherung von Cookies), erfolgt die Verarbeitung ausschließlich auf Grundlage von Art. 6
|
||||
Abs. 1 lit. a DSGVO; die Einwilligung ist jederzeit widerrufbar.
|
||||
</p>
|
||||
<p>
|
||||
Wenn Ihr Browser Web Fonts nicht unterstützt,
|
||||
wird eine Standardschrift von Ihrem Computer genutzt.
|
||||
</p>
|
||||
<p>
|
||||
Weitere Informationen zu Google Web Fonts finden Sie
|
||||
unter <a href="https://developers.google.com/fonts/faq" target="_blank" rel="noopener noreferrer">https://developers.google.com/fonts/faq</a>
|
||||
und in der Datenschutzerklärung von Google: <a href="https://policies.google.com/privacy?hl=de" target="_blank"
|
||||
rel="noopener noreferrer">https://policies.google.com/privacy?hl=de</a>.
|
||||
</p>
|
||||
|
||||
<h3>Font Awesome</h3>
|
||||
<p>
|
||||
Diese Seite nutzt zur einheitlichen Darstellung von Schriftarten und Symbolen Font Awesome.
|
||||
Anbieter ist die Fonticons, Inc., 6 Porter Road Apartment 3R, Cambridge, Massachusetts, USA.
|
||||
</p>
|
||||
<p>
|
||||
Beim Aufruf
|
||||
einer Seite lädt Ihr Browser die benötigten Fonts in ihren Browsercache, um Texte, Schriftarten und
|
||||
Symbole korrekt anzuzeigen. Zu diesem Zweck muss der von Ihnen verwendete Browser Verbindung zu den Servern von Font
|
||||
Awesome aufnehmen. Hierdurch erlangt Font Awesome Kenntnis darüber, dass über Ihre IP-Adresse diese
|
||||
Website aufgerufen wurde. Die Nutzung von Font Awesome erfolgt auf Grundlage von Art. 6 Abs. 1 lit. f DSGVO. Wir
|
||||
haben ein berechtigtes Interesse an der einheitlichen Darstellung des Schriftbildes auf unserer Website. Sofern eine
|
||||
entsprechende Einwilligung abgefragt wurde (z. B. eine Einwilligung zur Speicherung von Cookies), erfolgt die
|
||||
Verarbeitung ausschließlich auf Grundlage von Art. 6 Abs. 1 lit. a DSGVO; die Einwilligung ist jederzeit
|
||||
widerrufbar.
|
||||
</p>
|
||||
<p>
|
||||
Wenn Ihr Browser Font Awesome nicht unterstützt, wird eine Standardschrift von Ihrem
|
||||
Computer genutzt.
|
||||
</p>
|
||||
<p>
|
||||
Weitere Informationen zu Font Awesome finden Sie und in der Datenschutzerklärung
|
||||
von Font Awesome unter: <a href="https://fontawesome.com/privacy" target="_blank" rel="noopener noreferrer">https://fontawesome.com/privacy</a>.
|
||||
</p>
|
||||
</div>
|
||||
<app-footer></app-footer>
|
25
Frontend/src/app/pages/privacy/privacy.component.spec.ts
Normal file
25
Frontend/src/app/pages/privacy/privacy.component.spec.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PrivacyComponent } from './privacy.component';
|
||||
|
||||
describe('PrivacyComponent', () => {
|
||||
let component: PrivacyComponent;
|
||||
let fixture: ComponentFixture<PrivacyComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PrivacyComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PrivacyComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
15
Frontend/src/app/pages/privacy/privacy.component.ts
Normal file
15
Frontend/src/app/pages/privacy/privacy.component.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-privacy',
|
||||
templateUrl: './privacy.component.html',
|
||||
styleUrls: ['./privacy.component.css']
|
||||
})
|
||||
export class PrivacyComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
|
@ -1,13 +1,16 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Betterzon</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Betterzon</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
<script src="node_modules/cookieconsent/build/cookieconsent.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -4,3 +4,6 @@ body {
|
|||
font-family: sans-serif;
|
||||
margin-bottom: 10em;
|
||||
}
|
||||
|
||||
html, body { height: 100%; }
|
||||
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
|
||||
|
|
|
@ -4,7 +4,8 @@ Blog: https://blog.betterzon.xyz<br>
|
|||
Wiki: https://github.com/Mueller-Patrick/Betterzon/wiki
|
||||
|
||||
# Project Status
|
||||
[![build status](https://github.com/Mueller-Patrick/Betterzon/workflows/Build/badge.svg)](https://github.com/Mueller-Patrick/Betterzon/actions?query=branch%3Amaster)
|
||||
|
||||
# How to build
|
||||
Run ```ng build --prod``` to build a production version of the project
|
||||
![Latest Commit Build Status](https://ci.betterzon.xyz/job/Verify_Build_on_PR/badge/icon?style=flat-square&subject=Latest%20Commit)
|
||||
![Deployment Status](https://ci.betterzon.xyz/job/GitHub%20Deployment/badge/icon?style=flat-square&subject=Deployment&status=Success)
|
||||
<br>
|
||||
[![Website Status](https://img.shields.io/website?label=www.betterzon.xyz&style=for-the-badge&url=https%3A%2F%2Fwww.betterzon.xyz)](https://www.betterzon.xyz)
|
||||
[![Blog Status](https://img.shields.io/website?label=blog.betterzon.xyz&style=for-the-badge&url=https%3A%2F%2Fblog.betterzon.xyz)](https://blog.betterzon.xyz)
|
||||
|
|
Loading…
Reference in New Issue
Block a user