Merge branch 'feature/bootstrap5-migration' into 'main'
feat: обновление API для поиска автомобиля по VIN - изменен маршрут на... See merge request root/salvagedb!1
6
.cursorignore
Normal file
@ -0,0 +1,6 @@
|
||||
gips.txt
|
||||
ips.txt
|
||||
killapp.sh
|
||||
refresh_ip.sh
|
||||
sync_cloud_nginx.sh
|
||||
watchdog.sh
|
||||
4
.gitignore
vendored
@ -1,2 +1,4 @@
|
||||
__pycache__
|
||||
__pycache__/
|
||||
*.pyc
|
||||
logs
|
||||
node_modules/
|
||||
|
||||
126
api/swagger.yaml
@ -77,10 +77,10 @@ paths:
|
||||
type: string
|
||||
description: Error description
|
||||
|
||||
/decode:
|
||||
/v2/search:
|
||||
post:
|
||||
summary: Decode VIN
|
||||
description: Decode VIN number to get vehicle information
|
||||
summary: Search vehicle by VIN (v2)
|
||||
description: Search for vehicle information with full history records
|
||||
parameters:
|
||||
- name: access_code
|
||||
in: query
|
||||
@ -101,21 +101,77 @@ paths:
|
||||
example: "1HGCM82633A123456"
|
||||
responses:
|
||||
'200':
|
||||
description: Successful decoding
|
||||
description: Successful search
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
found:
|
||||
type: boolean
|
||||
description: Whether the vehicle was found
|
||||
details:
|
||||
type: object
|
||||
description: Vehicle details
|
||||
properties:
|
||||
make:
|
||||
type: string
|
||||
description: Vehicle make
|
||||
description: Vehicle manufacturer
|
||||
model:
|
||||
type: string
|
||||
description: Vehicle model
|
||||
year:
|
||||
type: integer
|
||||
description: Manufacturing year
|
||||
body:
|
||||
type: string
|
||||
description: Vehicle body type
|
||||
engine:
|
||||
type: string
|
||||
description: Engine model
|
||||
cylinders:
|
||||
type: string
|
||||
description: Number of engine cylinders
|
||||
drive:
|
||||
type: string
|
||||
description: Drive type (FWD/RWD/AWD)
|
||||
records:
|
||||
type: array
|
||||
description: Vehicle history records
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
odometer:
|
||||
type: integer
|
||||
description: Odometer reading in miles
|
||||
odometer_status:
|
||||
type: string
|
||||
description: Odometer status (ACTUAL, EXEMPT, NOT ACTUAL)
|
||||
title:
|
||||
type: string
|
||||
description: Title status (CLEAR, SALVAGE, REBUILT)
|
||||
damage1:
|
||||
type: string
|
||||
description: Primary damage type
|
||||
damage2:
|
||||
type: string
|
||||
description: Secondary damage type
|
||||
add_to_db:
|
||||
type: string
|
||||
format: date
|
||||
description: Date added to database
|
||||
RD_Status:
|
||||
type: string
|
||||
description: Vehicle status
|
||||
Sale_Location:
|
||||
type: string
|
||||
description: Sale location
|
||||
Repear_Cost:
|
||||
type: number
|
||||
description: Repair cost in USD
|
||||
Photo_Count:
|
||||
type: integer
|
||||
description: Number of available photos
|
||||
'400':
|
||||
description: Invalid VIN or access code
|
||||
content:
|
||||
@ -127,26 +183,19 @@ paths:
|
||||
type: string
|
||||
description: Error description
|
||||
|
||||
/detail/{vin}:
|
||||
get:
|
||||
summary: Get detailed information
|
||||
description: Get detailed information about a vehicle by VIN
|
||||
/v2/reqphoto:
|
||||
post:
|
||||
summary: Get vehicle photos
|
||||
description: Get photos of the vehicle by VIN
|
||||
parameters:
|
||||
- name: vin
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Vehicle Identification Number
|
||||
- name: access_code
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: API access code for authentication
|
||||
responses:
|
||||
'200':
|
||||
description: Successful information retrieval
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
@ -154,30 +203,37 @@ paths:
|
||||
properties:
|
||||
vin:
|
||||
type: string
|
||||
description: VIN number
|
||||
make:
|
||||
type: string
|
||||
description: Vehicle make
|
||||
model:
|
||||
type: string
|
||||
description: Vehicle model
|
||||
year:
|
||||
type: integer
|
||||
description: Manufacturing year
|
||||
history:
|
||||
description: Vehicle Identification Number
|
||||
example: "1HGCM82633A123456"
|
||||
responses:
|
||||
'200':
|
||||
description: Successful photo retrieval
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
found:
|
||||
type: boolean
|
||||
description: Whether photos were found
|
||||
photos:
|
||||
type: array
|
||||
description: List of photo information
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
url:
|
||||
type: string
|
||||
description: URL of the photo
|
||||
type:
|
||||
type: string
|
||||
description: Type of photo (FRONT, REAR, SIDE, INTERIOR, etc.)
|
||||
date:
|
||||
type: string
|
||||
format: date
|
||||
description: Event date
|
||||
event:
|
||||
type: string
|
||||
description: Event description
|
||||
'404':
|
||||
description: Vehicle not found
|
||||
description: Date when photo was taken
|
||||
'400':
|
||||
description: Invalid VIN or access code
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
|
||||
556
app.py
@ -13,16 +13,29 @@ import json
|
||||
from expiring_dict import ExpiringDict
|
||||
import uuid
|
||||
from flask_swagger_ui import get_swaggerui_blueprint
|
||||
import datetime
|
||||
from reportlab.pdfgen import canvas
|
||||
from reportlab.lib.pagesizes import A4
|
||||
from reportlab.lib import colors
|
||||
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer, Image
|
||||
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
||||
from reportlab.lib.units import inch, mm
|
||||
from reportlab.pdfgen.canvas import Canvas
|
||||
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
|
||||
from reportlab.platypus import Frame
|
||||
import sys
|
||||
import logging
|
||||
|
||||
|
||||
|
||||
|
||||
capcha_score: float = 0.1
|
||||
capcha_site = '6LcJpHMgAAAAAMQLNY_g8J2Kv_qmCGureRN_lbGl'
|
||||
capcha_site_sec = '6LcJpHMgAAAAAIUf4Jg_7NvawQKZoLoVypDU6-d8'
|
||||
capcha_site_url='https://www.google.com/recaptcha/api/siteverify'
|
||||
site = 'salvagedb.com'
|
||||
app_path = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
app_debug : bool = os.environ.get('APP_DEBUG',False)
|
||||
@ -32,7 +45,7 @@ app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1)
|
||||
|
||||
os.environ['NLS_LANG'] = 'American_America.AL32UTF8'
|
||||
|
||||
#Cache
|
||||
|
||||
app.cache = ExpiringDict(60*60*24)
|
||||
|
||||
# Swagger UI
|
||||
@ -68,34 +81,104 @@ def save_request(request):
|
||||
req_data['remote_addr'] = request.remote_addr
|
||||
return req_data
|
||||
|
||||
dictConfig(
|
||||
{
|
||||
# Создаем директорию для логов если её нет
|
||||
log_dir = os.path.join(app_path, 'logs')
|
||||
if not os.path.exists(log_dir):
|
||||
os.makedirs(log_dir)
|
||||
|
||||
# Определяем уровень логирования в зависимости от окружения
|
||||
log_level = 'DEBUG' if app_debug else 'INFO'
|
||||
|
||||
dictConfig({
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"formatters": {
|
||||
"default": {
|
||||
"format": "[%(asctime)s] [%(levelname)s | %(module)s] %(message)s",
|
||||
"datefmt": " %d/%m/%Y %H:%M:%S"
|
||||
"format": "[%(asctime)s] [%(levelname)s] [%(name)s:%(lineno)d] %(message)s",
|
||||
"datefmt": "%Y-%m-%d %H:%M:%S"
|
||||
},
|
||||
"error": {
|
||||
"format": "[%(asctime)s] [%(levelname)s] [%(name)s:%(lineno)d] [%(pathname)s] %(message)s",
|
||||
"datefmt": "%Y-%m-%d %H:%M:%S"
|
||||
}
|
||||
},
|
||||
"handlers": {
|
||||
"console": {
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "default",
|
||||
"stream": "ext://sys.stdout"
|
||||
},
|
||||
"file": {
|
||||
"class": "logging.handlers.TimedRotatingFileHandler",
|
||||
"filename": app_path + "/logs/app.log",
|
||||
"when": "D",
|
||||
"interval": 10,
|
||||
"backupCount": 5,
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"formatter": "default",
|
||||
"filename": os.path.join(log_dir, "app.log"),
|
||||
"maxBytes": 10485760, # 10MB
|
||||
"backupCount": 10,
|
||||
"encoding": "utf8"
|
||||
},
|
||||
|
||||
},
|
||||
"root": {"level": "DEBUG", "handlers": ["console", "file"]},
|
||||
"error_file": {
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"formatter": "error",
|
||||
"filename": os.path.join(log_dir, "error.log"),
|
||||
"maxBytes": 10485760, # 10MB
|
||||
"backupCount": 10,
|
||||
"encoding": "utf8",
|
||||
"level": "ERROR"
|
||||
}
|
||||
)
|
||||
},
|
||||
"loggers": {
|
||||
"werkzeug": {
|
||||
"handlers": ["console", "file"],
|
||||
"level": log_level,
|
||||
"propagate": False
|
||||
},
|
||||
"flask": {
|
||||
"handlers": ["console", "file"],
|
||||
"level": log_level,
|
||||
"propagate": False
|
||||
},
|
||||
"app": {
|
||||
"handlers": ["console", "file", "error_file"],
|
||||
"level": log_level,
|
||||
"propagate": False
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"level": log_level,
|
||||
"handlers": ["console", "file", "error_file"]
|
||||
}
|
||||
})
|
||||
|
||||
# Создаем логгер для приложения
|
||||
logger = logging.getLogger("app")
|
||||
|
||||
class OverlayCanvas(Canvas):
|
||||
def __init__(self, *args, **kwargs):
|
||||
Canvas.__init__(self, *args, **kwargs)
|
||||
self._saved_page_states = []
|
||||
|
||||
def showPage(self):
|
||||
self._saved_page_states.append(dict(self.__dict__))
|
||||
self._startPage()
|
||||
|
||||
def save(self):
|
||||
for state in self._saved_page_states:
|
||||
self.__dict__.update(state)
|
||||
self.draw_overlay() # теперь рисуем поверх
|
||||
Canvas.showPage(self)
|
||||
Canvas.save(self)
|
||||
|
||||
def draw_overlay(self):
|
||||
stamp_path = os.path.join(app_path, 'static', 'stamp256.png')
|
||||
if os.path.exists(stamp_path):
|
||||
self.drawImage(
|
||||
stamp_path,
|
||||
x=400, # настроить по ширине
|
||||
y=400, # настроить по высоте
|
||||
width=100,
|
||||
height=100,
|
||||
mask='auto'
|
||||
)
|
||||
|
||||
@app.after_request
|
||||
def after_request(response):
|
||||
@ -151,7 +234,7 @@ def sitemaps_xml(sitemap_id):
|
||||
sitemap_pages = cur.fetchall()
|
||||
return render_template('sitemaps.xml', site=site, sitemap_pages=sitemap_pages)
|
||||
except:
|
||||
app.logger.error(traceback.format_exc())
|
||||
logger.error(traceback.format_exc())
|
||||
return 'bad request!', 500
|
||||
|
||||
@app.route("/")
|
||||
@ -169,7 +252,7 @@ def index_html():
|
||||
app.cache['maxnum'] = cnt
|
||||
return render_template('index.html', site=site, cnt=cnt ,capcha_site=capcha_site)
|
||||
except:
|
||||
app.logger.error(traceback.format_exc())
|
||||
logger.error(traceback.format_exc())
|
||||
return 'bad request!', 500
|
||||
|
||||
@app.route("/privacy.html")
|
||||
@ -178,7 +261,7 @@ def privacy_html():
|
||||
|
||||
return render_template('privacy.html', site=site)
|
||||
except:
|
||||
app.logger.error(traceback.format_exc())
|
||||
logger.error(traceback.format_exc())
|
||||
return 'bad request!', 500
|
||||
|
||||
@app.route("/robots.txt")
|
||||
@ -186,7 +269,7 @@ def robot_txt():
|
||||
try:
|
||||
return render_template('robots.txt', site=site)
|
||||
except:
|
||||
app.logger.error(traceback.format_exc())
|
||||
logger.error(traceback.format_exc())
|
||||
return 'bad request!', 500
|
||||
|
||||
@app.route("/decode", methods = ['POST'])
|
||||
@ -195,13 +278,13 @@ def decode():
|
||||
vin = request.form.get('q').strip()
|
||||
g_respone = request.form['g-recaptcha-response']
|
||||
capcha_check = requests.post(url=f'{capcha_site_url}?secret={capcha_site_sec}&response={g_respone}').json()
|
||||
if capcha_check['success'] == False or capcha_check['score'] <0.5:
|
||||
app.logger.info(f'Google reuest: {capcha_site_url}?secret={capcha_site_sec}&response={g_respone}')
|
||||
app.logger.info(f'Bad google answer: {capcha_check}')
|
||||
if capcha_check['success'] == False or capcha_check['score'] <capcha_score:
|
||||
logger.info(f'Google reuest: {capcha_site_url}?secret={capcha_site_sec}&response={g_respone}')
|
||||
logger.info(f'Bad google answer: {capcha_check}')
|
||||
abort(401)
|
||||
return redirect(f'/detail/{vin}.html', 301)
|
||||
except:
|
||||
app.logger.error(traceback.format_exc())
|
||||
logger.error(traceback.format_exc())
|
||||
return 'bad request!', 500
|
||||
|
||||
|
||||
@ -210,7 +293,7 @@ def decodevin_html():
|
||||
try:
|
||||
return render_template('decodevin.html', site=site,capcha_site=capcha_site)
|
||||
except:
|
||||
app.logger.error(traceback.format_exc())
|
||||
logger.error(traceback.format_exc())
|
||||
return 'bad request!', 500
|
||||
|
||||
@app.route("/database/page<int:page_num>.html")
|
||||
@ -224,7 +307,7 @@ def database_page_prc(page_num):
|
||||
returnVal = cur.callfunc("checkip", int, [user_ip, request.headers.get("CF-IPCountry", 'None'), get_addr(user_ip), 1, 0, 0])
|
||||
except:
|
||||
print(request)
|
||||
app.logger.error(traceback.format_exc())
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
cur.execute('select max(pg) from mv_pages')
|
||||
max_page = int(cur.fetchall()[0][0])
|
||||
@ -238,7 +321,7 @@ def database_page_prc(page_num):
|
||||
pgf = cur.fetchall()
|
||||
return render_template('database.html', site=site, cur_page=pg, max_page=max_page, pg=pgf)
|
||||
except:
|
||||
app.logger.error(traceback.format_exc())
|
||||
logger.error(traceback.format_exc())
|
||||
return 'bad request!', 500
|
||||
|
||||
@app.route("/detail/<vin>.html")
|
||||
@ -251,7 +334,7 @@ def detail_vin(vin):
|
||||
returnVal = cur.callfunc("checkip", int, [user_ip, request.headers.get("CF-IPCountry", 'None'), get_addr(user_ip), 0, 0, 1])
|
||||
except:
|
||||
print(request)
|
||||
app.logger.error(traceback.format_exc())
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
cur.execute("""select 'None', COALESCE((select value from m_JSONS_FROM_NHTSA v3 where v3.svin =s.svin and v3.variableid ='26'),(select val from vind2 where svin = substr(s.vin, 1, 8) || '*' || substr(s.vin, 10, 2) and varb = 'Make'),'UNKNOWN') make,
|
||||
COALESCE((select value from m_JSONS_FROM_NHTSA v3 where v3.svin =s.svin and v3.variableid ='28'),(select val from vind2 where svin = substr(s.vin, 1, 8) || '*' || substr(s.vin, 10, 2) and varb = 'Model'),'UNKNOWN') model,
|
||||
@ -264,7 +347,7 @@ def detail_vin(vin):
|
||||
res = cur.fetchall()
|
||||
return render_template('details.html', site=site, vin=vin, det=res, capcha_site=capcha_site)
|
||||
except:
|
||||
app.logger.error(traceback.format_exc())
|
||||
logger.error(traceback.format_exc())
|
||||
return 'bad request!', 500
|
||||
|
||||
|
||||
@ -274,17 +357,17 @@ def search():
|
||||
user_ip = get_ip(request) ## определение ip клиента
|
||||
vin = request.form.get('q')
|
||||
ua = request.headers.get('User-Agent')
|
||||
app.logger.info(f'AgeNt: {ua}')
|
||||
logger.info(f'AgeNt: {ua}')
|
||||
|
||||
g_respone = request.form.get('g-recaptcha-response')
|
||||
|
||||
capcha_check = requests.post(url=f'{capcha_site_url}?secret={capcha_site_sec}&response={g_respone}').json()
|
||||
if capcha_check['success'] == False or capcha_check['score'] <0.5:
|
||||
app.logger.info(f'Google reuest: {capcha_site_url}?secret={capcha_site_sec}&response={g_respone}')
|
||||
app.logger.info(f'Bad google answer: {capcha_check}')
|
||||
if capcha_check['success'] == False or capcha_check['score'] <capcha_score:
|
||||
logger.info(f'Google reuest: {capcha_site_url}?secret={capcha_site_sec}&response={g_respone}')
|
||||
logger.info(f'Bad google answer: {capcha_check}')
|
||||
if app_debug==True:
|
||||
req_data = save_request(request)
|
||||
app.logger.info(json.dumps(req_data, indent=4, default=str))
|
||||
logger.info(json.dumps(req_data, indent=4, default=str))
|
||||
return 'google recaptcha req low score', 401
|
||||
|
||||
|
||||
@ -305,9 +388,14 @@ def search():
|
||||
res = cur.fetchall()
|
||||
cur.execute("""select rownum, t.vin, t.title, t.odo, t.odos, t.dem1, t.dem2, t.year||'/'||t.month from salvagedb t where vin = :p1 and svin = substr(:p1,1,10) """, {'p1': vin})
|
||||
his = cur.fetchall()
|
||||
|
||||
# Сохраняем VIN в сессии
|
||||
session['last_searched_vin'] = vin
|
||||
session['last_search_time'] = datetime.datetime.now().timestamp()
|
||||
|
||||
return render_template('search.html', site=site, vin=vin, det=res, his=his)
|
||||
except:
|
||||
app.logger.error(traceback.format_exc())
|
||||
logger.error(traceback.format_exc())
|
||||
return 'bad request!', 500
|
||||
|
||||
## API
|
||||
@ -328,7 +416,7 @@ def api_search():
|
||||
mimetype='application/json'
|
||||
)
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
return response
|
||||
if len(access_code) > 16 or access_code is None:
|
||||
ret = {'status': 'incorrect access_code'}
|
||||
@ -338,7 +426,7 @@ def api_search():
|
||||
mimetype='application/json'
|
||||
)
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
return response
|
||||
|
||||
conn = pool.acquire()
|
||||
@ -363,7 +451,7 @@ def api_search():
|
||||
mimetype='application/json'
|
||||
)
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
return response
|
||||
## ищем в истории
|
||||
cur.execute('select t.odo,t.odos,t.title,t.dem1,t.dem2,t.year,t.month from salvagedb t where vin =:p1 and svin = :p2',
|
||||
@ -394,7 +482,7 @@ def api_search():
|
||||
mimetype='application/json'
|
||||
)
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
cur.execute("insert into billing(access_code, vin, ip, status, dt, cost) values (:p1, :p2, :p3, :p4, CAST(SYSTIMESTAMP AT TIME ZONE 'UTC' AS DATE), :p5)",
|
||||
{'p1': str(access_code), 'p2': str(vin), 'p3': str(user_ip), 'p4': 'FOUND', 'p5': found_price})
|
||||
conn.commit()
|
||||
@ -404,7 +492,7 @@ def api_search():
|
||||
# nor found
|
||||
ret = {'status': 'NOT FOUND', 'cost': notfound_price}
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
response = app.response_class(
|
||||
response=json.dumps(ret),
|
||||
status=200,
|
||||
@ -416,7 +504,7 @@ def api_search():
|
||||
return response
|
||||
|
||||
except:
|
||||
app.logger.error(traceback.format_exc())
|
||||
logger.error(traceback.format_exc())
|
||||
return 'bad request!', 500
|
||||
|
||||
|
||||
@ -431,7 +519,7 @@ def api_restfact():
|
||||
mimetype='application/json'
|
||||
)
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
return response
|
||||
|
||||
conn = pool.acquire()
|
||||
@ -450,7 +538,7 @@ def api_restfact():
|
||||
mimetype='application/json'
|
||||
)
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
return response
|
||||
else:
|
||||
ret = {
|
||||
@ -463,7 +551,7 @@ def api_restfact():
|
||||
mimetype='application/json'
|
||||
)
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
return response
|
||||
|
||||
@app.route("/api/restfact_detail")
|
||||
@ -477,7 +565,7 @@ def restfact_detail():
|
||||
mimetype='application/json'
|
||||
)
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
return response
|
||||
|
||||
conn = pool.acquire()
|
||||
@ -493,7 +581,7 @@ def restfact_detail():
|
||||
mimetype='application/json'
|
||||
)
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
return response
|
||||
cur.execute("select vin, ip, status, dt, cost from billing where access_code = :p1 and dt > CAST(SYSTIMESTAMP AT TIME ZONE 'UTC' AS DATE) - 45 and made = 1 order by id", {'p1': str(access_code)})
|
||||
res = cur.fetchall()
|
||||
@ -505,7 +593,7 @@ def restfact_detail():
|
||||
mimetype='application/json'
|
||||
)
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
return response
|
||||
else:
|
||||
dat = []
|
||||
@ -529,7 +617,7 @@ def restfact_detail():
|
||||
mimetype='application/json'
|
||||
)
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
return response
|
||||
|
||||
## API V2
|
||||
@ -551,7 +639,7 @@ def api_search_v2():
|
||||
mimetype='application/json'
|
||||
)
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
return response
|
||||
if len(access_code) > 16 or access_code is None:
|
||||
ret = {'status': 'incorrect access_code'}
|
||||
@ -561,7 +649,7 @@ def api_search_v2():
|
||||
mimetype='application/json'
|
||||
)
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
return response
|
||||
|
||||
conn = pool.acquire()
|
||||
@ -586,7 +674,7 @@ def api_search_v2():
|
||||
mimetype='application/json'
|
||||
)
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
return response
|
||||
## ищем в истории
|
||||
cur.execute('''select s.odo,s.odos,s.title,s.dem1,s.dem2,s.year,s.month, json_value(i.jdata, '$.Runs_Drive') RD_Status, json_value(i.jdata, '$.Locate') Sale_Location, json_value(i.jdata, '$.RepCost') as Repear_cost,
|
||||
@ -622,7 +710,7 @@ def api_search_v2():
|
||||
mimetype='application/json'
|
||||
)
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
cur.execute("insert into billing(access_code, vin, ip, status, dt, cost) values (:p1, :p2, :p3, :p4, CAST(SYSTIMESTAMP AT TIME ZONE 'UTC' AS DATE), :p5)",
|
||||
{'p1': str(access_code), 'p2': str(vin), 'p3': str(user_ip), 'p4': 'FOUND', 'p5': found_price})
|
||||
conn.commit()
|
||||
@ -632,7 +720,7 @@ def api_search_v2():
|
||||
# nor found
|
||||
ret = {'status': 'NOT FOUND', 'cost': notfound_price}
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
response = app.response_class(
|
||||
response=json.dumps(ret),
|
||||
status=200,
|
||||
@ -644,7 +732,7 @@ def api_search_v2():
|
||||
return response
|
||||
|
||||
except:
|
||||
app.logger.error(traceback.format_exc())
|
||||
logger.error(traceback.format_exc())
|
||||
return 'bad request!', 500
|
||||
|
||||
@app.route("/api/v2/reqphoto")
|
||||
@ -664,7 +752,7 @@ def api_reqimage():
|
||||
mimetype='application/json'
|
||||
)
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
return response
|
||||
if len(access_code) > 16 or access_code is None:
|
||||
ret = {'status': 'incorrect access_code'}
|
||||
@ -674,7 +762,7 @@ def api_reqimage():
|
||||
mimetype='application/json'
|
||||
)
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
return response
|
||||
|
||||
conn = pool.acquire()
|
||||
@ -698,10 +786,10 @@ def api_reqimage():
|
||||
mimetype='application/json'
|
||||
)
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
return response
|
||||
|
||||
cur.execute('select count(*) from salvagedb.salvage_images where vin = :p1 and fnd = 1' , {'p1':vin.upper()})
|
||||
cur.execute('select count(*) from salvagedb.salvage_images where vin = :p1 and fn = 1' , {'p1':vin.upper()})
|
||||
res = cur.fetchone()
|
||||
img_count = int(res[0])
|
||||
if img_count<1:
|
||||
@ -712,22 +800,20 @@ def api_reqimage():
|
||||
mimetype='application/json'
|
||||
)
|
||||
if app.debug:
|
||||
app.logger.debug(json.dumps(ret))
|
||||
logger.debug(json.dumps(ret))
|
||||
return response
|
||||
else:
|
||||
#req_id = uuid.uuid4().hex
|
||||
#cur.execute('select rownum,ipath from salvagedb.salvage_images where vin = :p1 and fnd = 1',{'p1':vin.upper()})
|
||||
cur.execute("""select GenImages('{}','{}') uid from dual""".format(access_code, vin.upper()))
|
||||
cur.execute("""select GenImages('{}','{}') as ui from dual""".format(access_code, vin.upper()))
|
||||
res = cur.fetchall()
|
||||
req_id = res[0]
|
||||
cur.execute("""select fake_name from salvage_images_req where req_id = '{}}'""".format(req_id))
|
||||
req_id = res[0][0]
|
||||
cur.execute('select fake_name from salvage_images_req where req_id = :p1', {'p1':req_id})
|
||||
res = cur.fetchall()
|
||||
images = []
|
||||
nm = 0
|
||||
for it in res:
|
||||
images.append({
|
||||
'num':nm,
|
||||
'url':'https://salimages.salvagedb/{}/{}'.format(req_id, it)
|
||||
'url':'https://salimages.salvagedb/{}/{}'.format(req_id, it[0])
|
||||
})
|
||||
nm = nm + 1
|
||||
ret = {'status': 'Image_found', 'Images':images}
|
||||
@ -736,6 +822,7 @@ def api_reqimage():
|
||||
status=200,
|
||||
mimetype='application/json'
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
|
||||
@ -744,25 +831,34 @@ def api_reqimage():
|
||||
|
||||
|
||||
except:
|
||||
app.logger.error(traceback.format_exc())
|
||||
logger.error(traceback.format_exc())
|
||||
return 'bad request!', 500
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@app.route("/ads.txt")
|
||||
def ads_txt():
|
||||
try:
|
||||
return render_template('ads.txt')
|
||||
except:
|
||||
app.logger.error(traceback.format_exc())
|
||||
logger.error(traceback.format_exc())
|
||||
return 'bad request!', 500
|
||||
|
||||
@app.route('/favicon.ico')
|
||||
def logo():
|
||||
return send_file(app_path+"/static/favicon.ico")
|
||||
|
||||
@app.route('/limit')
|
||||
def rate_limit():
|
||||
try:
|
||||
return render_template('rate_limit.html', site=site)
|
||||
except:
|
||||
logger.error(traceback.format_exc())
|
||||
return 'bad request!', 500
|
||||
|
||||
@app.route('/donate')
|
||||
def donate():
|
||||
return render_template('donate.html')
|
||||
|
||||
|
||||
def get_ip(req) -> str:
|
||||
if 'X-Forwarded-For' in req.headers:
|
||||
@ -783,6 +879,328 @@ def get_addr(req) -> str:
|
||||
def swagger_yaml():
|
||||
return send_from_directory('api', 'swagger.yaml')
|
||||
|
||||
@app.route('/static/<path:filename>')
|
||||
def serve_static(filename):
|
||||
try:
|
||||
# Проверка расширения файла
|
||||
allowed_extensions = {'.css', '.js', '.png', '.jpg', '.jpeg', '.gif', '.ico', '.svg', '.json'}
|
||||
file_ext = os.path.splitext(filename)[1].lower()
|
||||
|
||||
if file_ext not in allowed_extensions:
|
||||
logger.warning(f'Attempt to access forbidden file type: {filename}')
|
||||
return 'Access denied', 403
|
||||
|
||||
# Проверка пути на directory traversal
|
||||
safe_path = os.path.normpath(os.path.join('static', filename))
|
||||
if not safe_path.startswith('static'):
|
||||
logger.warning(f'Attempt to access file outside static directory: {filename}')
|
||||
return 'Access denied', 403
|
||||
|
||||
# Определение MIME-типа
|
||||
mime_types = {
|
||||
'.css': 'text/css',
|
||||
'.js': 'application/javascript',
|
||||
'.json': 'application/json',
|
||||
'.png': 'image/png',
|
||||
'.jpg': 'image/jpeg',
|
||||
'.jpeg': 'image/jpeg',
|
||||
'.gif': 'image/gif',
|
||||
'.ico': 'image/x-icon',
|
||||
'.svg': 'image/svg+xml'
|
||||
}
|
||||
|
||||
mime_type = mime_types.get(file_ext, 'application/octet-stream')
|
||||
|
||||
# Логирование доступа
|
||||
logger.info(f'Access to static file: {filename}')
|
||||
|
||||
response = make_response(send_from_directory('static', filename))
|
||||
response.headers['Content-Type'] = mime_type
|
||||
|
||||
# Специальные заголовки для PWA файлов
|
||||
if filename == 'manifest.json':
|
||||
response.headers['Content-Type'] = 'application/manifest+json'
|
||||
elif filename == 'sw.js':
|
||||
response.headers['Service-Worker-Allowed'] = '/'
|
||||
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f'Error accessing file {filename}: {str(e)}')
|
||||
return 'File not found', 404
|
||||
|
||||
@app.route("/salvagereport/<string:vin>")
|
||||
def generate_pdf_report(vin):
|
||||
try:
|
||||
# Проверяем наличие VIN в сессии и время последнего поиска
|
||||
if 'last_searched_vin' not in session or session['last_searched_vin'] != vin:
|
||||
logger.warning(f'Direct access attempt to report generation for VIN: {vin}')
|
||||
return 'Access denied', 403
|
||||
|
||||
# Проверяем время последнего поиска (не более 5 минут)
|
||||
if datetime.datetime.now().timestamp() - session['last_search_time'] > 300:
|
||||
logger.warning(f'Report generation attempt expired for VIN: {vin}')
|
||||
return 'Access denied', 403
|
||||
|
||||
conn = pool.acquire()
|
||||
cur = conn.cursor()
|
||||
user_ip = get_ip(request)
|
||||
|
||||
try:
|
||||
returnVal = cur.callfunc("checkip", int, [user_ip, request.headers.get("CF-IPCountry", 'None'), get_addr(user_ip), 1, 0, 0])
|
||||
except:
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
# Get vehicle details
|
||||
cur.execute("""select 'None', COALESCE((select value from m_JSONS_FROM_NHTSA v3 where v3.svin =s.svin and v3.variableid ='26'),(select val from vind2 where svin = substr(s.vin, 1, 8) || '*' || substr(s.vin, 10, 2) and varb = 'Make'),'UNKNOWN') make,
|
||||
COALESCE((select value from m_JSONS_FROM_NHTSA v3 where v3.svin =s.svin and v3.variableid ='28'),(select val from vind2 where svin = substr(s.vin, 1, 8) || '*' || substr(s.vin, 10, 2) and varb = 'Model'),'UNKNOWN') model,
|
||||
COALESCE((select value from m_JSONS_FROM_NHTSA v3 where v3.svin =s.svin and v3.variableid ='29'),(select val from vind2 where svin = substr(s.vin, 1, 8) || '*' || substr(s.vin, 10, 2) and varb = 'Model Year'),'UNKNOWN') year,
|
||||
COALESCE((select value from m_JSONS_FROM_NHTSA v3 where v3.svin =s.svin and v3.variableid ='5'),(select val from vind2 where svin = substr(s.vin, 1, 8) || '*' || substr(s.vin, 10, 2) and varb = 'Body Class'),'UNKNOWN') body,
|
||||
COALESCE((select value from m_JSONS_FROM_NHTSA v3 where v3.svin =s.svin and v3.variableid ='13'),(select val from vind2 where svin = substr(s.vin, 1, 8) || '*' || substr(s.vin, 10, 2) and varb = 'Engine Model'),'UNKNOWN') engine,
|
||||
COALESCE((select value from m_JSONS_FROM_NHTSA v3 where v3.svin =s.svin and v3.variableid ='9'),(select val from vind2 where svin = substr(s.vin, 1, 8) || '*' || substr(s.vin, 10, 2) and varb = 'Engine Number of Cylinders'),'UNKNOWN') celindr,
|
||||
COALESCE((select value from m_JSONS_FROM_NHTSA v3 where v3.svin =s.svin and v3.variableid ='15'),(select val from vind2 where svin = substr(s.vin, 1, 8) || '*' || substr(s.vin, 10, 2) and varb = 'Drive Type'),'-') drive
|
||||
from (select substr(:p1,1,10) svin, :p1 vin from dual) s """, {'p1': vin})
|
||||
det = cur.fetchall()
|
||||
|
||||
# Get salvage history
|
||||
cur.execute("""select rownum, t.vin, t.title, t.odo, t.odos, t.dem1, t.dem2, t.year||'/'||t.month from salvagedb t where vin = :p1 and svin = substr(:p1,1,10) """, {'p1': vin})
|
||||
his = cur.fetchall()
|
||||
|
||||
# Generate current date and report ID
|
||||
now = datetime.datetime.now()
|
||||
report_date = now.strftime("%Y-%m-%d") # Только дата без времени
|
||||
report_id = uuid.uuid4().hex[:8].upper()
|
||||
current_year = now.year
|
||||
|
||||
try:
|
||||
# Создаем буфер для PDF
|
||||
buffer = io.BytesIO()
|
||||
|
||||
# Создаем PDF документ
|
||||
doc = SimpleDocTemplate(
|
||||
buffer,
|
||||
pagesize=A4,
|
||||
rightMargin=20*mm,
|
||||
leftMargin=20*mm,
|
||||
topMargin=15*mm,
|
||||
bottomMargin=20*mm
|
||||
)
|
||||
|
||||
# Создаем стили для текста
|
||||
styles = getSampleStyleSheet()
|
||||
styles.add(ParagraphStyle(
|
||||
name='ReportTitle',
|
||||
parent=styles['Heading1'],
|
||||
fontSize=16,
|
||||
alignment=TA_CENTER
|
||||
))
|
||||
styles.add(ParagraphStyle(
|
||||
name='ReportSubtitle',
|
||||
parent=styles['Normal'],
|
||||
fontSize=12,
|
||||
alignment=TA_CENTER,
|
||||
textColor=colors.HexColor('#0066CC') # Синий цвет для акцента
|
||||
))
|
||||
styles.add(ParagraphStyle(
|
||||
name='Right',
|
||||
parent=styles['Normal'],
|
||||
fontSize=10,
|
||||
alignment=TA_RIGHT
|
||||
))
|
||||
styles.add(ParagraphStyle(
|
||||
name='Disclaimer',
|
||||
parent=styles['Normal'],
|
||||
fontSize=8,
|
||||
textColor=colors.gray
|
||||
))
|
||||
styles.add(ParagraphStyle(
|
||||
name='SalvageDBFooter',
|
||||
parent=styles['Normal'],
|
||||
fontSize=9,
|
||||
alignment=TA_CENTER,
|
||||
textColor=colors.HexColor('#0066CC'), # Синий цвет для акцента
|
||||
fontName='Helvetica-Bold'
|
||||
))
|
||||
|
||||
# Список элементов для PDF
|
||||
elements = []
|
||||
|
||||
# Путь к логотипу
|
||||
# logo_path = os.path.join(app_path, 'static', 'icons', 'icon-144x144.png')
|
||||
|
||||
# Добавляем логотип, если файл существует
|
||||
# if os.path.exists(logo_path):
|
||||
# logo_img = Image(logo_path, width=30*mm, height=30*mm)
|
||||
# logo_img.hAlign = 'CENTER'
|
||||
# elements.append(logo_img)
|
||||
# elements.append(Spacer(1, 5*mm))
|
||||
|
||||
# Заголовок отчета с акцентом на SALVAGEDB.COM
|
||||
elements.append(Paragraph("Vehicle History Report", styles['ReportTitle']))
|
||||
elements.append(Paragraph("SALVAGEDB.COM - Comprehensive Vehicle History Check", styles['ReportSubtitle']))
|
||||
elements.append(Spacer(1, 10*mm))
|
||||
|
||||
# Информация об отчете
|
||||
elements.append(Paragraph(f"Report Date: {report_date}", styles['Right']))
|
||||
elements.append(Paragraph(f"Report ID: {report_id}", styles['Right']))
|
||||
elements.append(Spacer(1, 15*mm))
|
||||
|
||||
# Информация о транспортном средстве
|
||||
elements.append(Paragraph("Vehicle Information", styles['Heading2']))
|
||||
|
||||
# Создаем таблицу с информацией о транспортном средстве
|
||||
vehicle_data = [
|
||||
["VIN", vin],
|
||||
["Make", det[0][1]],
|
||||
["Model", det[0][2]],
|
||||
["Year", det[0][3]],
|
||||
["Body Style", det[0][4]],
|
||||
["Engine", det[0][5]],
|
||||
["Cylinders", det[0][6]],
|
||||
["Drive", det[0][7]]
|
||||
]
|
||||
|
||||
vehicle_table = Table(vehicle_data, colWidths=[100, 350])
|
||||
vehicle_table.setStyle(TableStyle([
|
||||
('BACKGROUND', (0, 0), (0, -1), colors.lightgrey),
|
||||
('TEXTCOLOR', (0, 0), (0, -1), colors.black),
|
||||
('ALIGN', (0, 0), (0, -1), 'LEFT'),
|
||||
('FONTNAME', (0, 0), (0, -1), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0, 0), (0, -1), 10),
|
||||
('BOTTOMPADDING', (0, 0), (-1, -1), 6),
|
||||
('BACKGROUND', (1, 0), (-1, -1), colors.white),
|
||||
('GRID', (0, 0), (-1, -1), 0.5, colors.black),
|
||||
]))
|
||||
|
||||
elements.append(vehicle_table)
|
||||
elements.append(Spacer(1, 10*mm))
|
||||
|
||||
# Информация об истории
|
||||
if his:
|
||||
elements.append(Paragraph("Salvage History", styles['Heading2']))
|
||||
|
||||
# Создаем стиль для ячеек с переносом текста
|
||||
table_style = TableStyle([
|
||||
('BACKGROUND', (0, 0), (-1, 0), colors.lightgrey),
|
||||
('TEXTCOLOR', (0, 0), (-1, 0), colors.black),
|
||||
('ALIGN', (0, 0), (-1, 0), 'CENTER'),
|
||||
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0, 0), (-1, 0), 9),
|
||||
('BOTTOMPADDING', (0, 0), (-1, 0), 6),
|
||||
('BACKGROUND', (0, 1), (-1, -1), colors.white),
|
||||
('GRID', (0, 0), (-1, -1), 0.5, colors.black),
|
||||
('VALIGN', (0, 0), (-1, -1), 'TOP'),
|
||||
('LEFTPADDING', (0, 0), (-1, -1), 3),
|
||||
('RIGHTPADDING', (0, 0), (-1, -1), 3),
|
||||
('FONTSIZE', (0, 1), (-1, -1), 8),
|
||||
('WORDWRAP', (0, 0), (-1, -1), True),
|
||||
])
|
||||
|
||||
# Оптимизированные заголовки без VIN
|
||||
history_headers = ["Title", "Mileage", "Odometer Status", "Primary\nDamage", "Secondary\nDamage"]
|
||||
|
||||
# Формируем данные для таблицы истории
|
||||
history_data = [history_headers]
|
||||
|
||||
for item in his:
|
||||
title_value = item[2]
|
||||
primary_damage = item[5] if item[5] else "N/A"
|
||||
secondary_damage = item[6] if item[6] else "N/A"
|
||||
|
||||
# Добавляем строку с данными, убрав VIN
|
||||
history_data.append([
|
||||
title_value,
|
||||
str(item[3]),
|
||||
item[4],
|
||||
primary_damage,
|
||||
secondary_damage
|
||||
])
|
||||
|
||||
# Устанавливаем ширину колонок оптимально для A4
|
||||
available_width = doc.width # Доступная ширина страницы
|
||||
col_widths = [
|
||||
available_width * 0.20, # 20% для Title
|
||||
available_width * 0.12, # 12% для Mileage
|
||||
available_width * 0.18, # 18% для Odometer Status
|
||||
available_width * 0.25, # 25% для Primary Damage
|
||||
available_width * 0.25 # 25% для Secondary Damage
|
||||
]
|
||||
|
||||
# Создаем таблицу с правильными размерами
|
||||
history_table = Table(history_data, colWidths=col_widths, repeatRows=1)
|
||||
history_table.setStyle(table_style)
|
||||
|
||||
elements.append(history_table)
|
||||
else:
|
||||
elements.append(Paragraph("Salvage History", styles['Heading2']))
|
||||
elements.append(Paragraph("Salvage history not found.", styles['Normal']))
|
||||
|
||||
elements.append(Spacer(1, 15*mm))
|
||||
|
||||
# Добавляем штамп поверх таблицы
|
||||
# stamp_path = os.path.join(app_path, 'static', 'stamp256.png')
|
||||
# if os.path.exists(stamp_path):
|
||||
# stamp_img = Image(stamp_path, width=100, height=100)
|
||||
# stamp_img.hAlign = 'RIGHT'
|
||||
# stamp_img.vAlign = 'TOP'
|
||||
# # Размещаем штамп поверх таблицы
|
||||
# elements.append(stamp_img)
|
||||
|
||||
# elements.append(Spacer(1, 15*mm))
|
||||
|
||||
|
||||
|
||||
# Дисклеймер
|
||||
disclaimer_text = """
|
||||
Disclaimer: This report provides information about salvage or junk vehicles; damage from hail, flood or fire;
|
||||
mileage discrepancies or odometer rollback; and gray market vehicles. We do not claim that the car got in our
|
||||
databank has salvage title, but the fact that it has been damaged for sure. Our site helps people avoid buying
|
||||
a damaged vehicle in the past.
|
||||
"""
|
||||
elements.append(Paragraph(disclaimer_text, styles['Disclaimer']))
|
||||
|
||||
# Футер с акцентом на SALVAGEDB.COM
|
||||
elements.append(Spacer(1, 15*mm))
|
||||
elements.append(Paragraph(f"© {current_year} SALVAGEDB.COM - All Rights Reserved", styles['SalvageDBFooter']))
|
||||
elements.append(Paragraph("Visit SALVAGEDB.COM for more information about vehicle history", styles['SalvageDBFooter']))
|
||||
elements.append(Paragraph('"This report is provided as is without any guarantees or warranty."', styles['Disclaimer']))
|
||||
|
||||
# Строим PDF документ
|
||||
# doc.build(elements, onFirstPage=add_stamp_overlay, onLaterPages=add_stamp_overlay)
|
||||
doc.build(elements, canvasmaker=OverlayCanvas)
|
||||
|
||||
# Получаем содержимое буфера
|
||||
pdf_data = buffer.getvalue()
|
||||
buffer.close()
|
||||
|
||||
# Создаем HTTP-ответ с PDF
|
||||
response = make_response(pdf_data)
|
||||
response.headers['Content-Type'] = 'application/pdf'
|
||||
response.headers['Content-Disposition'] = f'attachment; filename=vehicle_report_{vin}.pdf'
|
||||
|
||||
return response
|
||||
|
||||
except Exception as pdf_error:
|
||||
# Если возникла ошибка при генерации PDF, логируем ее
|
||||
app.logger.error(f"PDF generation error: {str(pdf_error)}")
|
||||
app.logger.error(traceback.format_exc())
|
||||
|
||||
# Показываем печатную версию в браузере
|
||||
return render_template(
|
||||
'report_printable.html',
|
||||
vin=vin,
|
||||
det=det,
|
||||
his=his,
|
||||
report_date=report_date,
|
||||
report_id=report_id,
|
||||
current_year=current_year,
|
||||
error_message=str(pdf_error)
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
app.logger.error(traceback.format_exc())
|
||||
return 'Report generation failed: ' + str(e), 500
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Start a pool of connections
|
||||
pool = start_pool()
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
[33m1799493[m[33m ([m[1;36mHEAD[m[33m -> [m[1;32mmain[m[33m)[m feat: адаптация шаблонов для мобильных устройств
|
||||
[33mc2d60d9[m[33m ([m[1;31morigin/main[m[33m, [m[1;31morigin/HEAD[m[33m)[m Развиваем API v2: +search Поправил favicon
|
||||
[33m11f6d56[m Перевод search на post
|
||||
[33m97e85ec[m remove readme.md
|
||||
[33m12f0b91[m Init
|
||||
9
package.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "salvagedb-web",
|
||||
"version": "1.0.0",
|
||||
"description": "SalvageDB Web Application",
|
||||
"dependencies": {
|
||||
"bootstrap": "^5.3.2",
|
||||
"@popperjs/core": "^2.11.8"
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
flask
|
||||
oracledb
|
||||
cacheing
|
||||
reportlab
|
||||
9
static/bootstrap-responsive.min.css
vendored
12
static/bootstrap.min.css
vendored
BIN
static/icons/icon-128x128.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
static/icons/icon-144x144.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
static/icons/icon-152x152.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
static/icons/icon-192x192.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
static/icons/icon-384x384.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
static/icons/icon-512x512.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
static/icons/icon-72x72.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
static/icons/icon-96x96.png
Normal file
|
After Width: | Height: | Size: 9.8 KiB |
BIN
static/logo2.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
51
static/manifest.json
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "Salvagedb",
|
||||
"short_name": "Salvagedb",
|
||||
"description": "Check vehicle history and salvage information",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#ffffff",
|
||||
"theme_color": "#0d6efd",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/static/icons/icon-72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/static/icons/icon-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/static/icons/icon-128x128.png",
|
||||
"sizes": "128x128",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/static/icons/icon-144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/static/icons/icon-152x152.png",
|
||||
"sizes": "152x152",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/static/icons/icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/static/icons/icon-384x384.png",
|
||||
"sizes": "384x384",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/static/icons/icon-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
static/salvagedblogo.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
static/stamp256.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
static/stamp_big.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
82
static/sw.js
Normal file
@ -0,0 +1,82 @@
|
||||
const CACHE_NAME = 'salvagedb-v1';
|
||||
const urlsToCache = [
|
||||
'/',
|
||||
'/static/styles.css',
|
||||
'/static/styles__ltr.css',
|
||||
'/static/privacy.css',
|
||||
'/static/slogo.png',
|
||||
'/static/logo2.png',
|
||||
'/static/salvagedblogo.png',
|
||||
'/static/favicon.ico',
|
||||
'/static/faviconV2.png',
|
||||
'/static/curved-arrow.png',
|
||||
'/static/vin-position1.gif',
|
||||
'/static/manifest.json',
|
||||
'/static/icons/icon-72x72.png',
|
||||
'/static/icons/icon-96x96.png',
|
||||
'/static/icons/icon-128x128.png',
|
||||
'/static/icons/icon-144x144.png',
|
||||
'/static/icons/icon-152x152.png',
|
||||
'/static/icons/icon-192x192.png',
|
||||
'/static/icons/icon-384x384.png',
|
||||
'/static/icons/icon-512x512.png',
|
||||
'https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css',
|
||||
'https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js',
|
||||
'https://www.google.com/recaptcha/api.js'
|
||||
];
|
||||
|
||||
self.addEventListener('install', event => {
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME)
|
||||
.then(cache => {
|
||||
console.log('Opened cache');
|
||||
return Promise.all(
|
||||
urlsToCache.map(url =>
|
||||
fetch(url)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch ${url}`);
|
||||
}
|
||||
return cache.put(url, response);
|
||||
})
|
||||
.catch(error => {
|
||||
console.warn(`Failed to cache ${url}:`, error);
|
||||
})
|
||||
)
|
||||
);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Cache failed:', error);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', event => {
|
||||
event.respondWith(
|
||||
caches.match(event.request)
|
||||
.then(response => {
|
||||
if (response) {
|
||||
return response;
|
||||
}
|
||||
return fetch(event.request)
|
||||
.then(response => {
|
||||
if (!response || response.status !== 200 || response.type !== 'basic') {
|
||||
return response;
|
||||
}
|
||||
const responseToCache = response.clone();
|
||||
caches.open(CACHE_NAME)
|
||||
.then(cache => {
|
||||
cache.put(event.request, responseToCache);
|
||||
});
|
||||
return response;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Fetch failed:', error);
|
||||
return new Response('Network error happened', {
|
||||
status: 408,
|
||||
headers: { 'Content-Type': 'text/plain' },
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
39
templates/base.html
Normal file
@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}{{ site }}{% endblock %}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="{{ url_for('index_html') }}">{{ site }}</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('index_html') }}">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('decodevin_html') }}">Decode VIN</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('donate') }}">Support Us</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@ -1,35 +1,34 @@
|
||||
{% extends "head.html" %}
|
||||
|
||||
{% block title %}Salvage Vehicles Database{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="hero-unit">
|
||||
<h1>Buying a Used Car? Check it!</h1>
|
||||
<br>
|
||||
<p><a href="https://www.salvagedb.com/" title="Salvagedb.com" rel="nofollow">Salvagedb.com</a> provides
|
||||
<div class="container-lg py-4">
|
||||
<div class="p-5 mb-4 bg-light rounded-3">
|
||||
<h1 class="display-4">Buying a Used Car? Check it!</h1>
|
||||
<p class="lead">
|
||||
<a href="https://www.salvagedb.com/" title="Salvagedb.com" rel="nofollow">Salvagedb.com</a> provides
|
||||
information about salvage or junk vehicles; damage from hail, flood or fire; mileage discrepancies or
|
||||
odometer rollback; and gray market vehicles. We do not claim that the car got in our databank has salvage
|
||||
title, but the fact that it has been damaged for sure. Our site helps people avoid buying a damaged vehicle
|
||||
in the past.
|
||||
</p>
|
||||
</div>
|
||||
<br><br>
|
||||
|
||||
<div class="table-container">
|
||||
<table id="hor-minimalist-a" summary="Salvage cars;Database page 1">
|
||||
<thead>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>VIN</th>
|
||||
<th>Make</th>
|
||||
<th>Model</th>
|
||||
<th>Year</th>
|
||||
<th scope="col">VIN</th>
|
||||
<th scope="col">Make</th>
|
||||
<th scope="col">Model</th>
|
||||
<th scope="col">Year</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in pg %}
|
||||
<tr>
|
||||
<td>{{ row[0] }}</td>
|
||||
<td><a href="/detail/{{ row[1] }}.html">{{ row[1] }}</a></td>
|
||||
<td><a href="/detail/{{ row[1] }}.html" class="text-decoration-none">{{ row[1] }}</a></td>
|
||||
<td>{{ row[2] }}</td>
|
||||
<td>{{ row[3] }}</td>
|
||||
<td>{{ row[4] }}</td>
|
||||
@ -39,16 +38,21 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<br><br>
|
||||
<div class="pagination-container">
|
||||
<div class="pagination">
|
||||
<nav aria-label="Page navigation" class="my-4">
|
||||
<ul class="pagination justify-content-center">
|
||||
{% if cur_page > 1 %}
|
||||
<a class="btn" href="/database/page1.html">First</a>
|
||||
<a class="btn" href="/database/page{{cur_page-1}}.html">Prev</a>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="/database/page1.html">First</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="/database/page{{cur_page-1}}.html">Prev</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if cur_page > 3 %}
|
||||
<span class="btn disabled">...</span>
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link">...</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% set start_page = cur_page - 2 %}
|
||||
@ -62,25 +66,32 @@
|
||||
{% endif %}
|
||||
|
||||
{% for page in range(start_page, end_page + 1) %}
|
||||
<li class="page-item {% if page == cur_page %}active{% endif %}">
|
||||
{% if page == cur_page %}
|
||||
<button class="btn active">{{page}}</button>
|
||||
<span class="page-link">{{page}}</span>
|
||||
{% else %}
|
||||
<a class="btn" href="/database/page{{page}}.html">{{page}}</a>
|
||||
<a class="page-link" href="/database/page{{page}}.html">{{page}}</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
{% if cur_page < max_page-2 %}
|
||||
<span class="btn disabled">...</span>
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link">...</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if cur_page < max_page %}
|
||||
<a class="btn" href="/database/page{{cur_page+1}}.html">Next</a>
|
||||
<a class="btn" href="/database/page{{max_page}}.html">Last</a>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="/database/page{{cur_page+1}}.html">Next</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="/database/page{{max_page}}.html">Last</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<hr>
|
||||
<hr class="my-4">
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@ -1,38 +1,48 @@
|
||||
{% extends "head.html" %}
|
||||
|
||||
{% block title %}Decode VIN Number{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="hero-unit">
|
||||
<h1>Buying a Used Car? Check it!</h1>
|
||||
<br>
|
||||
<p><a href="https://www.salvagedb.com/" title="Salvagedb.com" rel="nofollow">Salvagedb.com</a> provides
|
||||
<div class="container-lg py-4">
|
||||
<div class="p-5 mb-4 bg-light rounded-3">
|
||||
<h1 class="display-4">Buying a Used Car? Check it!</h1>
|
||||
<p class="lead">
|
||||
<a href="https://www.salvagedb.com/" title="Salvagedb.com" rel="nofollow">Salvagedb.com</a> provides
|
||||
information about salvage or junk vehicles; damage from hail, flood or fire; mileage discrepancies or
|
||||
odometer rollback; and gray market vehicles. We do not claim that the car got in our databank has salvage
|
||||
title, but the fact that it has been damaged for sure. Our site helps people avoid buying a damaged vehicle
|
||||
in the past.
|
||||
</p>
|
||||
</div>
|
||||
<br><br>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid">
|
||||
<center>
|
||||
<form id="search_bar" action="/decode" method="post">
|
||||
<div class="search-container">
|
||||
<div class="search-title">
|
||||
<h2 id="find_your_car">ENTER <a href="https://en.wikipedia.org/wiki/Vehicle_Identification_Number" target="_blank">VIN</a> YOU CAR</h2>
|
||||
<div class="container-lg">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<form id="search_bar" action="/decode" method="post" class="mb-4">
|
||||
<div class="card">
|
||||
<div class="card-header text-center">
|
||||
<h2 class="h4 mb-0">ENTER <a href="https://en.wikipedia.org/wiki/Vehicle_Identification_Number" target="_blank" class="text-decoration-none">VIN</a> OF YOUR CAR</h2>
|
||||
</div>
|
||||
<div class="search-wrapper">
|
||||
<input type="text" id="vininput" name="q" class="search_box ui-autocomplete-input"
|
||||
autocomplete="off" value="" role="textbox" autofocus required
|
||||
<div class="card-body">
|
||||
<div class="input-group">
|
||||
<input type="text" id="vininput" name="q"
|
||||
class="form-control"
|
||||
autocomplete="off"
|
||||
placeholder="Enter VIN number"
|
||||
autofocus required
|
||||
validate="length_between,17,17">
|
||||
<button class="g-recaptcha btn btn-primary" type="submit"
|
||||
data-sitekey="{{capcha_site}}" id="go_button"
|
||||
data-callback='onSubmit' data-action='submit'>Check It</button>
|
||||
<button class="btn btn-primary g-recaptcha d-flex align-items-center justify-content-center"
|
||||
type="submit"
|
||||
data-sitekey="{{capcha_site}}"
|
||||
id="go_button"
|
||||
data-callback='onSubmit'
|
||||
data-action='submit'>Check It</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -41,5 +51,4 @@
|
||||
document.getElementById("search_bar").submit();
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
{% extends "head.html" %}
|
||||
|
||||
{% block title %}Vehicle Details{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="hero-unit">
|
||||
<h1>Buying a Used Car? Check it!</h1>
|
||||
<br>
|
||||
<p><a href="https://www.salvagedb.com/" title="Salvagedb.com" rel="nofollow">Salvagedb.com</a> provides
|
||||
<div class="container-lg py-4">
|
||||
<div class="p-5 mb-4 bg-light rounded-3">
|
||||
<h1 class="display-4">Buying a Used Car? Check it!</h1>
|
||||
<p class="lead">
|
||||
<a href="https://www.salvagedb.com/" title="Salvagedb.com" rel="nofollow">Salvagedb.com</a> provides
|
||||
information about salvage or junk vehicles; damage from hail, flood or fire; mileage discrepancies or
|
||||
odometer rollback; and gray market vehicles. We do not claim that the car got in our databank has salvage
|
||||
title, but the fact that it has been damaged for sure. Our site helps people avoid buying a damaged vehicle
|
||||
@ -13,59 +15,73 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="details-container">
|
||||
<div class="car-details">
|
||||
<table id="one-column-emphasis">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2 class="h4 mb-0">Vehicle Details</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Make</td>
|
||||
<th scope="row">Make</th>
|
||||
<td>{{det[0][1]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Model</td>
|
||||
<th scope="row">Model</th>
|
||||
<td>{{det[0][2]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Year</td>
|
||||
<th scope="row">Year</th>
|
||||
<td>{{det[0][3]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Body Style</td>
|
||||
<th scope="row">Body Style</th>
|
||||
<td>{{det[0][4]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Engine</td>
|
||||
<th scope="row">Engine</th>
|
||||
<td>{{det[0][5]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Cylinders</td>
|
||||
<th scope="row">Cylinders</th>
|
||||
<td>{{det[0][6]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Drive</td>
|
||||
<th scope="row">Drive</th>
|
||||
<td>{{det[0][7]}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-container">
|
||||
<h2>Search salvage history?</h2>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2 class="h4 mb-0">Search salvage history?</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form name="search_bar" id="search_bar" action="/search" method="post">
|
||||
<div class="search-wrapper">
|
||||
<input id="make_model" class="search_box ui-autocomplete-input" name="q"
|
||||
value="{{vin}}" autocomplete="off" spellcheck="false" type="text">
|
||||
<button class="g-recaptcha btn btn-primary" data-sitekey="{{capcha_site}}"
|
||||
<div class="input-group">
|
||||
<input id="make_model" class="form-control" name="q"
|
||||
value="{{vin}}" autocomplete="off" spellcheck="false" type="text"
|
||||
placeholder="Enter VIN number">
|
||||
<button class="g-recaptcha btn btn-primary d-flex align-items-center justify-content-center" data-sitekey="{{capcha_site}}"
|
||||
id="go_button" data-callback='onSubmit' data-action='submit'>Search</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function onSubmit(token) {
|
||||
document.getElementById("search_bar").submit();
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
{% endblock %}
|
||||
67
templates/donate.html
Normal file
@ -0,0 +1,67 @@
|
||||
{% extends "head.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card shadow">
|
||||
<div class="card-body text-center">
|
||||
<h1 class="card-title mb-4">Support SalvageDB</h1>
|
||||
|
||||
<div class="mb-5">
|
||||
<p class="lead">
|
||||
For 15 years, we have been providing free access to vehicle history information,
|
||||
helping people make informed decisions when purchasing used vehicles.
|
||||
</p>
|
||||
<p>
|
||||
Our database contains information about damaged, salvaged, and rebuilt
|
||||
vehicles, helping to avoid purchasing a car with hidden issues.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-5">
|
||||
<h2 class="h4 mb-3">Support Our Project</h2>
|
||||
<p>
|
||||
Your support helps us continue providing quality service and developing
|
||||
our database. We accept donations in cryptocurrency.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h3 class="h5 mb-3">Bitcoin</h3>
|
||||
<img src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=16uL5x6qx8yF7k4uxyQVfz5N7yLE9F3iCZ"
|
||||
alt="Bitcoin QR Code" class="img-fluid mb-3">
|
||||
<p class="mb-2">Wallet Address:</p>
|
||||
<code class="d-block mb-3">16uL5x6qx8yF7k4uxyQVfz5N7yLE9F3iCZ</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h3 class="h5 mb-3">TonCoin</h3>
|
||||
<img src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=UQDi0mgMaDqXORp7UzV6n7E5WFC0yre0on63BSO2tIa7umFe"
|
||||
alt="TonCoin QR Code" class="img-fluid mb-3">
|
||||
<p class="mb-2">Wallet Address:</p>
|
||||
<code class="d-block mb-3">UQDi0mgMaDqXORp7UzV6n7E5WFC0yre0on63BSO2tIa7umFe</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<p class="text-muted">
|
||||
Thank you for your support! Every donation helps us improve our service
|
||||
and provide better information to our users.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -15,15 +15,14 @@
|
||||
<meta name="google-site-verification" content="NBTledQfL0wfsD78Ro4s-mdYTEMhXUQGBpsy4aLor9M">
|
||||
<meta property="fb:admins" content="100000893732418">
|
||||
<meta http-equiv="Content-Language" content="en">
|
||||
<title>Vehicle history check. Check your car VIN. For FREE!</title>
|
||||
<link href="https://storage.googleapis.com/static.salvagedb.com/favicon.ico" rel="shortcut icon"
|
||||
type="image/x-icon">
|
||||
<link href="/static/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/bootstrap-responsive.min.css" rel="stylesheet">
|
||||
<link href="/static/plus.css" rel="stylesheet">
|
||||
<link href="/static/plus2.css" rel="stylesheet">
|
||||
<title>{% block title %}{% endblock %} - {{ site }}</title>
|
||||
<link href="/static/icons/icon-72x72.png" rel="shortcut icon"
|
||||
type="image/png">
|
||||
<link rel="manifest" href="/static/manifest.json">
|
||||
<meta name="theme-color" content="#0d6efd">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<link href="/static/styles.css" rel="stylesheet">
|
||||
<script src="/static/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- Yatomo -->
|
||||
<script type="text/javascript" >
|
||||
@ -44,7 +43,6 @@
|
||||
<!-- End Matomo Code -->
|
||||
|
||||
<script src="https://www.google.com/recaptcha/api.js"></script>
|
||||
<script src="/static/bootstrap.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var btnNavbar = document.querySelector('.btn-navbar');
|
||||
@ -67,32 +65,55 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker.register('/static/sw.js')
|
||||
.then(registration => {
|
||||
console.log('ServiceWorker registration successful');
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('ServiceWorker registration failed: ', err);
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<link rel="stylesheet"
|
||||
href="data:text/css;charset=utf-8;base64,LyogU2V0IHRoZSBhcHAgYXR0cmlidXRlIHRvIHlvdXIgYXBwJ3MgZGFzaC1kZWxpbWl0ZWQgYWxpYXMuICovCmNsb3VkZmxhcmUtYXBwW2FwcD0ieW91ci1hcHAtbmFtZSJdIHsKICBkaXNwbGF5OiBibG9jazsKICBtYXJnaW46IDAuNWVtIDAuMmVtOwogIG91dGxpbmU6IDFweCBkb3R0ZWQ7CiAgcGFkZGluZzogMC41ZW07Cn0KCi8qIFVzZSBuYXRpdmUgZWxlbWVudHMgd2hlbiB5b3UnZCBsaWtlIHRvIGluaGVyaXQgc29tZSBzdHlsZXMgZnJvbSB0aGUgcGFnZS4gKi8KY2xvdWRmbGFyZS1hcHBbYXBwPSJ5b3VyLWFwcC1uYW1lIl0gcCB7CiAgdGV4dC1pbmRlbnQ6IDA7Cn0KCi8qIFVzZSBlbSB1bml0cyB0byBzY2FsZSBlbGVtZW50cyBmb3IgZGlmZmVyZW50IGluaGVyaXRlZCBzdHlsZXMuICovCmNsb3VkZmxhcmUtYXBwW2FwcD0ieW91ci1hcHAtbmFtZSJdIGgxIHsKICBmb250LXNpemU6IDEuOGVtOwogIGZvbnQtd2VpZ2h0OiBib2xkOwp9CgovKiBVc2UgY3VzdG9tIGVsZW1lbnRzIHRvIGF2b2lkIHN0eWxlIGluaGVyaXRhbmNlLiAqLwpjbG91ZGZsYXJlLWFwcFthcHA9InlvdXItYXBwLW5hbWUiXSBleGFtcGxlLWJ1dHRvbiB7CiAgYm9yZGVyOiAxcHggc29saWQ7Cn0KCi8qIFByZWZpeCBjbGFzc2VzIHdpdGggeW91ciBhcHAncyBhbGlhcyB0byBhdm9pZCBzdHlsZSBjb2xsaXNpb25zLiAqLwpjbG91ZGZsYXJlLWFwcFthcHA9InlvdXItYXBwLW5hbWUiXSAuZXhhbXBsZS1oaWdobGlnaHRlZCB7CiAgYmFja2dyb3VuZC1jb2xvcjogeWVsbG93OwogIG91dGxpbmU6IDAuMmVtIHNvbGlkIHllbGxvdzsKfQo=">
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
<body data-new-gr-c-s-check-loaded="14.982.0">
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<div class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
|
||||
<div class="container-lg">
|
||||
<a class="navbar-brand" href="https://www.salvagedb.com/">
|
||||
<img src="/static/slogo.png" height="28" alt="Salvagedb">
|
||||
Salvagedb.com
|
||||
</a>
|
||||
<a class="brand" href="https://www.salvagedb.com/">
|
||||
<img src="/static/slogo.png" height="28">
|
||||
Salvagedb.com</a>
|
||||
<div class="nav-collapse">
|
||||
<ul class="nav">
|
||||
<li class="active"><a href="/">Home</a></li>
|
||||
<li class="no2"><a href="/database/page1.html">Salvage vehicles</a></li>
|
||||
<li class="no3"><a href="/decodevin.html">Decode VIN</a></li>
|
||||
<li class="no6"><a href="mailto:info@salvagedb.com">Contact</a></li>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/database/page1.html">Vehicle History</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/decodevin.html">Decode VIN</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('donate') }}">Support Us</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="mailto:info@salvagedb.com">Contact</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
|
||||
@ -104,5 +125,8 @@
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap 5 Bundle with Popper -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -1,47 +1,55 @@
|
||||
{% extends "head.html" %}
|
||||
|
||||
{% block title %}Buying a Used Car? Check it!{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="hero-unit">
|
||||
<h1>Buying a Used Car? Check it!</h1>
|
||||
<br>
|
||||
<p><a href="https://www.salvagedb.com/" title="Salvagedb.com" rel="nofollow">Salvagedb.com</a> provides
|
||||
<div class="container-lg py-4">
|
||||
<div class="p-5 mb-4 bg-light rounded-3">
|
||||
<h1 class="display-4">Buying a Used Car? Check it!</h1>
|
||||
<p class="lead">
|
||||
<a href="https://www.salvagedb.com/" title="Salvagedb.com" rel="nofollow">Salvagedb.com</a> provides
|
||||
information about salvage or junk vehicles; damage from hail, flood or fire; mileage discrepancies or
|
||||
odometer rollback; and gray market vehicles. We do not claim that the car got in our databank has salvage
|
||||
title, but the fact that it has been damaged for sure. Our site helps people avoid buying a damaged vehicle
|
||||
in the past.
|
||||
</p>
|
||||
</div>
|
||||
<br><br>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid">
|
||||
<center>
|
||||
<form id="search_bar" action="/decode" method="post">
|
||||
<div class="search-container">
|
||||
<div class="search-title">
|
||||
<h2 id="find_your_car">ENTER <a href="https://en.wikipedia.org/wiki/Vehicle_Identification_Number" target="_blank">VIN</a> YOU CAR</h2>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<form id="search_bar" action="/decode" method="post" class="mb-4">
|
||||
<div class="card">
|
||||
<div class="card-header text-center">
|
||||
<h2 class="h4 mb-0">ENTER <a href="https://en.wikipedia.org/wiki/Vehicle_Identification_Number" target="_blank" class="text-decoration-none">VIN</a> OF YOUR CAR</h2>
|
||||
</div>
|
||||
<div class="search-wrapper">
|
||||
<input type="text" id="vininput" name="q" class="search_box ui-autocomplete-input"
|
||||
autocomplete="off" value="" role="textbox" autofocus required
|
||||
<div class="card-body">
|
||||
<div class="input-group">
|
||||
<input type="text" id="vininput" name="q"
|
||||
class="form-control"
|
||||
style="width: 80%;"
|
||||
autocomplete="off"
|
||||
placeholder="Enter VIN number"
|
||||
autofocus required
|
||||
validate="length_between,17,17">
|
||||
<button class="g-recaptcha btn btn-primary" type="submit"
|
||||
data-sitekey="{{capcha_site}}" id="go_button"
|
||||
data-callback='onSubmit' data-action='submit'>Check It</button>
|
||||
<button class="btn btn-primary g-recaptcha d-flex align-items-center justify-content-center"
|
||||
type="submit"
|
||||
data-sitekey="{{capcha_site}}"
|
||||
id="go_button"
|
||||
data-callback='onSubmit'
|
||||
data-action='submit'>Check It</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 style="text-align: center;">Over {{ cnt }} salvage vehicles added.</h2>
|
||||
<br>
|
||||
<center>
|
||||
<img src="static/vin-position1.gif" class="vin-image">
|
||||
</center>
|
||||
<br>
|
||||
<h2 class="text-center mb-4">Over {{ cnt }} salvage vehicles added.</h2>
|
||||
<div class="text-center">
|
||||
<img src="static/vin-position1.gif" class="img-fluid" alt="VIN position diagram">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function onSubmit(token) {
|
||||
document.getElementById("search_bar").submit();
|
||||
|
||||
@ -1,290 +1,89 @@
|
||||
{% raw %}
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html id="iubenda_policy" class="iubenda_fixed_policy">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>Privacy Policy of www.salvagedb.com</title>
|
||||
<meta content="privacy policy" name="keywords">
|
||||
<meta http-equiv="Content-Language" content="en">
|
||||
<meta name="robots" content="noindex, follow">
|
||||
{% extends "head.html" %}
|
||||
|
||||
<meta property="og:title" content="Privacy Policy of www.salvagedb.com">
|
||||
<meta property="og:type" content="website">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<link href="static/privacy.css" media="screen" rel="stylesheet" type="text/css">
|
||||
<style>@media print {#ghostery-purple-box {display:none !important}}</style>
|
||||
<link rel="stylesheet"
|
||||
href="data:text/css;charset=utf-8;base64,LyogU2V0IHRoZSBhcHAgYXR0cmlidXRlIHRvIHlvdXIgYXBwJ3MgZGFzaC1kZWxpbWl0ZWQgYWxpYXMuICovCmNsb3VkZmxhcmUtYXBwW2FwcD0ieW91ci1hcHAtbmFtZSJdIHsKICBkaXNwbGF5OiBibG9jazsKICBtYXJnaW46IDAuNWVtIDAuMmVtOwogIG91dGxpbmU6IDFweCBkb3R0ZWQ7CiAgcGFkZGluZzogMC41ZW07Cn0KCi8qIFVzZSBuYXRpdmUgZWxlbWVudHMgd2hlbiB5b3UnZCBsaWtlIHRvIGluaGVyaXQgc29tZSBzdHlsZXMgZnJvbSB0aGUgcGFnZS4gKi8KY2xvdWRmbGFyZS1hcHBbYXBwPSJ5b3VyLWFwcC1uYW1lIl0gcCB7CiAgdGV4dC1pbmRlbnQ6IDA7Cn0KCi8qIFVzZSBlbSB1bml0cyB0byBzY2FsZSBlbGVtZW50cyBmb3IgZGlmZmVyZW50IGluaGVyaXRlZCBzdHlsZXMuICovCmNsb3VkZmxhcmUtYXBwW2FwcD0ieW91ci1hcHAtbmFtZSJdIGgxIHsKICBmb250LXNpemU6IDEuOGVtOwogIGZvbnQtd2VpZ2h0OiBib2xkOwp9CgovKiBVc2UgY3VzdG9tIGVsZW1lbnRzIHRvIGF2b2lkIHN0eWxlIGluaGVyaXRhbmNlLiAqLwpjbG91ZGZsYXJlLWFwcFthcHA9InlvdXItYXBwLW5hbWUiXSBleGFtcGxlLWJ1dHRvbiB7CiAgYm9yZGVyOiAxcHggc29saWQ7Cn0KCi8qIFByZWZpeCBjbGFzc2VzIHdpdGggeW91ciBhcHAncyBhbGlhcyB0byBhdm9pZCBzdHlsZSBjb2xsaXNpb25zLiAqLwpjbG91ZGZsYXJlLWFwcFthcHA9InlvdXItYXBwLW5hbWUiXSAuZXhhbXBsZS1oaWdobGlnaHRlZCB7CiAgYmFja2dyb3VuZC1jb2xvcjogeWVsbG93OwogIG91dGxpbmU6IDAuMmVtIHNvbGlkIHllbGxvdzsKfQo=">
|
||||
</head>
|
||||
<body>
|
||||
<div id="wbars_all">
|
||||
<div class="iub_container iub_base_container">
|
||||
<div id="wbars">
|
||||
<div class="iub_content legal_pp">
|
||||
<div class="iub_header">
|
||||
<h1>Privacy Policy of <strong>www.salvagedb.com</strong></h1>
|
||||
<p>In order to receive information about your Personal Data, the purposes and the parties the Data
|
||||
is shared with, contact the Owner.</p>
|
||||
</div>
|
||||
<div class="one_line_col">
|
||||
<h2 id="owner_of_the_data">Owner and Data Controller</h2>
|
||||
</div>
|
||||
<div class="one_line_col">
|
||||
<h2 id="types_of_data">Types of Data collected</h2>
|
||||
{% block title %}Privacy Policy{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-lg py-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h1 class="display-4 mb-4">Privacy Policy of <strong>www.salvagedb.com</strong></h1>
|
||||
<p class="lead">In order to receive information about your Personal Data, the purposes and the parties the Data is shared with, contact the Owner.</p>
|
||||
|
||||
<h2 class="h3 mt-4" id="owner_of_the_data">Owner and Data Controller</h2>
|
||||
|
||||
<h2 class="h3 mt-4" id="types_of_data">Types of Data collected</h2>
|
||||
<p>The owner does not provide a list of Personal Data types collected.</p>
|
||||
<p>Complete details on each type of Personal Data collected are provided in the dedicated sections
|
||||
of this privacy policy or by specific explanation texts displayed prior to the Data
|
||||
collection.<br>Personal Data may be freely provided by the User, or, in case of Usage Data,
|
||||
collected automatically when using this Application.<br>Unless specified otherwise, all Data
|
||||
requested by this Application is mandatory and failure to provide this Data may make it
|
||||
impossible for this Application to provide its services. In cases where this Application
|
||||
specifically states that some Data is not mandatory, Users are free not to communicate this Data
|
||||
without consequences to the availability or the functioning of the Service.<br>Users who are
|
||||
uncertain about which Personal Data is mandatory are welcome to contact the Owner.<br>Any use of
|
||||
Cookies <20> or of other tracking tools <20> by this Application or by the owners of third-party
|
||||
services used by this Application serves the purpose of providing the Service required by the
|
||||
User, in addition to any other purposes described in the present document and in the Cookie
|
||||
Policy, if available.</p>
|
||||
<p>Users are responsible for any third-party Personal Data obtained, published or shared through
|
||||
this Application and confirm that they have the third party's consent to provide the Data to the
|
||||
Owner.</p>
|
||||
</div>
|
||||
<div class="one_line_col">
|
||||
<h2 id="place_of_processing">Mode and place of processing the Data</h2>
|
||||
<h3>Methods of processing</h3>
|
||||
<p>The Owner takes appropriate security measures to prevent unauthorized access, disclosure,
|
||||
modification, or unauthorized destruction of the Data.<br>The Data processing is carried out
|
||||
using computers and/or IT enabled tools, following organizational procedures and modes strictly
|
||||
related to the purposes indicated. In addition to the Owner, in some cases, the Data may be
|
||||
accessible to certain types of persons in charge, involved with the operation of this
|
||||
Application (administration, sales, marketing, legal, system administration) or external parties
|
||||
(such as third-party technical service providers, mail carriers, hosting providers, IT
|
||||
companies, communications agencies) appointed, if necessary, as Data Processors by the Owner.
|
||||
The updated list of these parties may be requested from the Owner at any time.</p>
|
||||
<h3>Legal basis of processing</h3>
|
||||
<p>Complete details on each type of Personal Data collected are provided in the dedicated sections of this privacy policy or by specific explanation texts displayed prior to the Data collection.<br>Personal Data may be freely provided by the User, or, in case of Usage Data, collected automatically when using this Application.<br>Unless specified otherwise, all Data requested by this Application is mandatory and failure to provide this Data may make it impossible for this Application to provide its services. In cases where this Application specifically states that some Data is not mandatory, Users are free not to communicate this Data without consequences to the availability or the functioning of the Service.<br>Users who are uncertain about which Personal Data is mandatory are welcome to contact the Owner.<br>Any use of Cookies – or of other tracking tools – by this Application or by the owners of third-party services used by this Application serves the purpose of providing the Service required by the User, in addition to any other purposes described in the present document and in the Cookie Policy, if available.</p>
|
||||
<p>Users are responsible for any third-party Personal Data obtained, published or shared through this Application and confirm that they have the third party's consent to provide the Data to the Owner.</p>
|
||||
|
||||
<h2 class="h3 mt-4" id="place_of_processing">Mode and place of processing the Data</h2>
|
||||
<h3 class="h4">Methods of processing</h3>
|
||||
<p>The Owner takes appropriate security measures to prevent unauthorized access, disclosure, modification, or unauthorized destruction of the Data.<br>The Data processing is carried out using computers and/or IT enabled tools, following organizational procedures and modes strictly related to the purposes indicated. In addition to the Owner, in some cases, the Data may be accessible to certain types of persons in charge, involved with the operation of this Application (administration, sales, marketing, legal, system administration) or external parties (such as third-party technical service providers, mail carriers, hosting providers, IT companies, communications agencies) appointed, if necessary, as Data Processors by the Owner. The updated list of these parties may be requested from the Owner at any time.</p>
|
||||
|
||||
<h3 class="h4">Legal basis of processing</h3>
|
||||
<p>The Owner may process Personal Data relating to Users if one of the following applies:</p>
|
||||
<ul>
|
||||
<li>Users have given their consent for one or more specific purposes. Note: Under some
|
||||
legislations the Owner may be allowed to process Personal Data until the User objects to
|
||||
such processing (<28>opt-out<75>), without having to rely on consent or any other of the following
|
||||
legal bases. This, however, does not apply, whenever the processing of Personal Data is
|
||||
subject to European data protection law;
|
||||
</li>
|
||||
<li>provision of Data is necessary for the performance of an agreement with the User and/or for
|
||||
any pre-contractual obligations thereof;
|
||||
</li>
|
||||
<li>processing is necessary for compliance with a legal obligation to which the Owner is
|
||||
subject;
|
||||
</li>
|
||||
<li>processing is related to a task that is carried out in the public interest or in the
|
||||
exercise of official authority vested in the Owner;
|
||||
</li>
|
||||
<li>processing is necessary for the purposes of the legitimate interests pursued by the Owner or
|
||||
by a third party.
|
||||
</li>
|
||||
<ul class="list-unstyled">
|
||||
<li class="mb-2">• Users have given their consent for one or more specific purposes. Note: Under some legislations the Owner may be allowed to process Personal Data until the User objects to such processing ("opt-out"), without having to rely on consent or any other of the following legal bases. This, however, does not apply, whenever the processing of Personal Data is subject to European data protection law;</li>
|
||||
<li class="mb-2">• provision of Data is necessary for the performance of an agreement with the User and/or for any pre-contractual obligations thereof;</li>
|
||||
<li class="mb-2">• processing is necessary for compliance with a legal obligation to which the Owner is subject;</li>
|
||||
<li class="mb-2">• processing is related to a task that is carried out in the public interest or in the exercise of official authority vested in the Owner;</li>
|
||||
<li class="mb-2">• processing is necessary for the purposes of the legitimate interests pursued by the Owner or by a third party.</li>
|
||||
</ul>
|
||||
<p>In any case, the Owner will gladly help to clarify the specific legal basis that applies to the
|
||||
processing, and in particular whether the provision of Personal Data is a statutory or
|
||||
contractual requirement, or a requirement necessary to enter into a contract. </p>
|
||||
<h3>Place</h3>
|
||||
<p>The Data is processed at the Owner's operating offices and in any other places where the parties
|
||||
involved in the processing are located.<br><br>
|
||||
Depending on the User's location, data transfers may involve transferring the User's Data to a
|
||||
country other than their own. To find out more about the place of processing of such transferred
|
||||
Data, Users can check the section containing details about the processing of Personal Data.</p>
|
||||
<p>Users are also entitled to learn about the legal basis of Data transfers to a country outside the
|
||||
European Union or to any international organization governed by public international law or set
|
||||
up by two or more countries, such as the UN, and about the security measures taken by the Owner
|
||||
to safeguard their Data.<br><br>
|
||||
If any such transfer takes place, Users can find out more by checking the relevant sections of
|
||||
this document or inquire with the Owner using the information provided in the contact section.
|
||||
</p>
|
||||
<h3>Retention time</h3>
|
||||
<p>Personal Data shall be processed and stored for as long as required by the purpose they have been
|
||||
collected for.</p>
|
||||
<p>Therefore:</p>
|
||||
<ul>
|
||||
<li>Personal Data collected for purposes related to the performance of a contract between the
|
||||
Owner and the User shall be retained until such contract has been fully performed.
|
||||
</li>
|
||||
<li>Personal Data collected for the purposes of the Owner<65>s legitimate interests shall be
|
||||
retained as long as needed to fulfill such purposes. Users may find specific information
|
||||
regarding the legitimate interests pursued by the Owner within the relevant sections of this
|
||||
document or by contacting the Owner.
|
||||
</li>
|
||||
</ul>
|
||||
<p>The Owner may be allowed to retain Personal Data for a longer period whenever the User has given
|
||||
consent to such processing, as long as such consent is not withdrawn. Furthermore, the Owner may
|
||||
be obliged to retain Personal Data for a longer period whenever required to do so for the
|
||||
performance of a legal obligation or upon order of an authority.<br><br>
|
||||
Once the retention period expires, Personal Data shall be deleted. Therefore, the right to
|
||||
access, the right to erasure, the right to rectification and the right to data portability
|
||||
cannot be enforced after expiration of the retention period.</p>
|
||||
</div>
|
||||
<div class="one_line_col">
|
||||
<h2>The rights of Users</h2>
|
||||
|
||||
<h2 class="h3 mt-4">The rights of Users</h2>
|
||||
<p>Users may exercise certain rights regarding their Data processed by the Owner.</p>
|
||||
<p>In particular, Users have the right to do the following:</p>
|
||||
<ul>
|
||||
<li><b>Withdraw their consent at any time.</b> Users have the right to withdraw consent where
|
||||
they have previously given their consent to the processing of their Personal Data.
|
||||
</li>
|
||||
<li><b>Object to processing of their Data.</b> Users have the right to object to the processing
|
||||
of their Data if the processing is carried out on a legal basis other than consent. Further
|
||||
details are provided in the dedicated section below.
|
||||
</li>
|
||||
<li><b>Access their Data.</b> Users have the right to learn if Data is being processed by the
|
||||
Owner, obtain disclosure regarding certain aspects of the processing and obtain a copy of
|
||||
the Data undergoing processing.
|
||||
</li>
|
||||
<li><b>Verify and seek rectification.</b> Users have the right to verify the accuracy of their
|
||||
Data and ask for it to be updated or corrected.
|
||||
</li>
|
||||
<li><b>Restrict the processing of their Data.</b> Users have the right, under certain
|
||||
circumstances, to restrict the processing of their Data. In this case, the Owner will not
|
||||
process their Data for any purpose other than storing it.
|
||||
</li>
|
||||
<li><b>Have their Personal Data deleted or otherwise removed.</b> Users have the right, under
|
||||
certain circumstances, to obtain the erasure of their Data from the Owner.
|
||||
</li>
|
||||
<li><b>Receive their Data and have it transferred to another controller.</b> Users have the
|
||||
right to receive their Data in a structured, commonly used and machine readable format and,
|
||||
if technically feasible, to have it transmitted to another controller without any hindrance.
|
||||
This provision is applicable provided that the Data is processed by automated means and that
|
||||
the processing is based on the User's consent, on a contract which the User is part of or on
|
||||
pre-contractual obligations thereof.
|
||||
</li>
|
||||
<li><b>Lodge a complaint.</b> Users have the right to bring a claim before their competent data
|
||||
protection authority.
|
||||
</li>
|
||||
<ul class="list-unstyled">
|
||||
<li class="mb-2"><strong>Withdraw their consent at any time.</strong> Users have the right to withdraw consent where they have previously given their consent to the processing of their Personal Data.</li>
|
||||
<li class="mb-2"><strong>Object to processing of their Data.</strong> Users have the right to object to the processing of their Data if the processing is carried out on a legal basis other than consent. Further details are provided in the dedicated section below.</li>
|
||||
<li class="mb-2"><strong>Access their Data.</strong> Users have the right to learn if Data is being processed by the Owner, obtain disclosure regarding certain aspects of the processing and obtain a copy of the Data undergoing processing.</li>
|
||||
<li class="mb-2"><strong>Verify and seek rectification.</strong> Users have the right to verify the accuracy of their Data and ask for it to be updated or corrected.</li>
|
||||
<li class="mb-2"><strong>Restrict the processing of their Data.</strong> Users have the right, under certain circumstances, to restrict the processing of their Data. In this case, the Owner will not process their Data for any purpose other than storing it.</li>
|
||||
<li class="mb-2"><strong>Have their Personal Data deleted or otherwise removed.</strong> Users have the right, under certain circumstances, to obtain the erasure of their Data from the Owner.</li>
|
||||
<li class="mb-2"><strong>Receive their Data and have it transferred to another controller.</strong> Users have the right to receive their Data in a structured, commonly used and machine readable format and, if technically feasible, to have it transmitted to another controller without any hindrance. This provision is applicable provided that the Data is processed by automated means and that the processing is based on the User's consent, on a contract which the User is part of or on pre-contractual obligations thereof.</li>
|
||||
<li class="mb-2"><strong>Lodge a complaint.</strong> Users have the right to bring a claim before their competent data protection authority.</li>
|
||||
</ul>
|
||||
<h3>Details about the right to object to processing</h3>
|
||||
<p>Where Personal Data is processed for a public interest, in the exercise of an official authority
|
||||
vested in the Owner or for the purposes of the legitimate interests pursued by the Owner, Users
|
||||
may object to such processing by providing a ground related to their particular situation to
|
||||
justify the objection.</p>
|
||||
<p>Users must know that, however, should their Personal Data be processed for direct marketing
|
||||
purposes, they can object to that processing at any time without providing any justification. To
|
||||
learn, whether the Owner is processing Personal Data for direct marketing purposes, Users may
|
||||
refer to the relevant sections of this document. </p>
|
||||
<h3>How to exercise these rights</h3>
|
||||
<p>Any requests to exercise User rights can be directed to the Owner through the contact details
|
||||
provided in this document. These requests can be exercised free of charge and will be addressed
|
||||
by the Owner as early as possible and always within one month.</p>
|
||||
</div>
|
||||
<div class="one_line_col">
|
||||
<h2 id="further_data_processing_info">Additional information about Data collection and
|
||||
processing</h2>
|
||||
<h3>Legal action</h3>
|
||||
<p>
|
||||
The User's Personal Data may be used for legal purposes by the Owner in Court or in the stages
|
||||
leading to possible legal action arising from improper use of this Application or the related
|
||||
Services.<br>The User declares to be aware that the Owner may be required to reveal personal
|
||||
data upon request of public authorities.
|
||||
</p>
|
||||
<h3>Additional information about User's Personal Data</h3>
|
||||
<p>
|
||||
In addition to the information contained in this privacy policy, this Application may provide
|
||||
the User with additional and contextual information concerning particular Services or the
|
||||
collection and processing of Personal Data upon request.
|
||||
</p>
|
||||
<h3>System logs and maintenance</h3>
|
||||
<p>
|
||||
For operation and maintenance purposes, this Application and any third-party services may
|
||||
collect files that record interaction with this Application (System logs) use other Personal
|
||||
Data (such as the IP Address) for this purpose.
|
||||
</p>
|
||||
<h3>Information not contained in this policy</h3>
|
||||
<p>
|
||||
More details concerning the collection or processing of Personal Data may be requested from the
|
||||
Owner at any time. Please see the contact information at the beginning of this document.
|
||||
</p>
|
||||
<h3>How <20>Do Not Track<63> requests are handled</h3>
|
||||
<p>
|
||||
This Application does not support <20>Do Not Track<63> requests.<br>To determine whether any of the
|
||||
third-party services it uses honor the <20>Do Not Track<63> requests, please read their privacy
|
||||
policies.
|
||||
</p>
|
||||
<h3>Changes to this privacy policy</h3>
|
||||
<p>
|
||||
The Owner reserves the right to make changes to this privacy policy at any time by giving notice
|
||||
to its Users on this page and possibly within this Application and/or - as far as technically
|
||||
and legally feasible - sending a notice to Users via any contact information available to the
|
||||
Owner. It is strongly recommended to check this page often, referring to the date of the last
|
||||
modification listed at the bottom. <br><br>
|
||||
Should the changes affect processing activities performed on the basis of the User<65>s consent,
|
||||
the Owner shall collect new consent from the User, where required.
|
||||
</p>
|
||||
</div>
|
||||
<div class="one_line_col">
|
||||
<div class="box_primary box_10 definitions expand">
|
||||
<h3 class="expand-click w_icon_24 icon_ribbon">
|
||||
Definitions and legal references
|
||||
</h3>
|
||||
<div class="expand-content">
|
||||
<h4>Personal Data (or Data)</h4>
|
||||
<p>Any information that directly, indirectly, or in connection with other information <20>
|
||||
including a personal identification number <20> allows for the identification or
|
||||
identifiability of a natural person.</p>
|
||||
<h4>Usage Data</h4>
|
||||
<p>Information collected automatically through this Application (or third-party services
|
||||
employed in this Application), which can include: the IP addresses or domain names of
|
||||
the computers utilized by the Users who use this Application, the URI addresses (Uniform
|
||||
Resource Identifier), the time of the request, the method utilized to submit the request
|
||||
to the server, the size of the file received in response, the numerical code indicating
|
||||
the status of the server's answer (successful outcome, error, etc.), the country of
|
||||
origin, the features of the browser and the operating system utilized by the User, the
|
||||
various time details per visit (e.g., the time spent on each page within the
|
||||
Application) and the details about the path followed within the Application with special
|
||||
reference to the sequence of pages visited, and other parameters about the device
|
||||
operating system and/or the User's IT environment.</p>
|
||||
<h4>User</h4>
|
||||
<p>The individual using this Application who, unless otherwise specified, coincides with the
|
||||
Data Subject.</p>
|
||||
<h4>Data Subject</h4>
|
||||
<p>The natural person to whom the Personal Data refers.</p>
|
||||
<h4>Data Processor (or Data Supervisor)</h4>
|
||||
<p>The natural or legal person, public authority, agency or other body which processes
|
||||
Personal Data on behalf of the Controller, as described in this privacy policy.</p>
|
||||
<h4>Data Controller (or Owner)</h4>
|
||||
<p>The natural or legal person, public authority, agency or other body which, alone or
|
||||
jointly with others, determines the purposes and means of the processing of Personal
|
||||
Data, including the security measures concerning the operation and use of this
|
||||
Application. The Data Controller, unless otherwise specified, is the Owner of this
|
||||
Application.</p>
|
||||
<h4>
|
||||
This Application
|
||||
</h4>
|
||||
<p>The means by which the Personal Data of the User is collected and processed.</p>
|
||||
<h4>Service</h4>
|
||||
<p>The service provided by this Application as described in the relative terms (if
|
||||
available) and on this site/application.</p>
|
||||
<h4>European Union (or EU)
|
||||
</h4>
|
||||
<p>Unless otherwise specified, all references made within this document to the European
|
||||
Union include all current member states to the European Union and the European Economic
|
||||
Area.
|
||||
</p>
|
||||
<hr>
|
||||
<h4>Legal information</h4>
|
||||
<p>This privacy statement has been prepared based on provisions of multiple legislations,
|
||||
including Art. 13/14 of Regulation (EU) 2016/679 (General Data Protection
|
||||
Regulation).</p>
|
||||
<p>This privacy policy relates solely to this Application, if not stated otherwise within
|
||||
this document.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="iub_footer">
|
||||
<p>
|
||||
Latest update: May 13, 2018
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
{% endraw %}
|
||||
<h2 class="h3 mt-4" id="further_data_processing_info">Additional information about Data collection and processing</h2>
|
||||
<h3 class="h4">Legal action</h3>
|
||||
<p>The User's Personal Data may be used for legal purposes by the Owner in Court or in the stages leading to possible legal action arising from improper use of this Application or the related Services.<br>The User declares to be aware that the Owner may be required to reveal personal data upon request of public authorities.</p>
|
||||
|
||||
<h3 class="h4">Additional information about User's Personal Data</h3>
|
||||
<p>In addition to the information contained in this privacy policy, this Application may provide the User with additional and contextual information concerning particular Services or the collection and processing of Personal Data upon request.</p>
|
||||
|
||||
<h3 class="h4">System logs and maintenance</h3>
|
||||
<p>For operation and maintenance purposes, this Application and any third-party services may collect files that record interaction with this Application (System logs) use other Personal Data (such as the IP Address) for this purpose.</p>
|
||||
|
||||
<h3 class="h4">Information not contained in this policy</h3>
|
||||
<p>More details concerning the collection or processing of Personal Data may be requested from the Owner at any time. Please see the contact information at the beginning of this document.</p>
|
||||
|
||||
<h3 class="h4">How "Do Not Track" requests are handled</h3>
|
||||
<p>This Application does not support "Do Not Track" requests.<br>To determine whether any of the third-party services it uses honor the "Do Not Track" requests, please read their privacy policies.</p>
|
||||
|
||||
<h3 class="h4">Changes to this privacy policy</h3>
|
||||
<p>The Owner reserves the right to make changes to this privacy policy at any time by giving notice to its Users on this page and possibly within this Application and/or - as far as technically and legally feasible - sending a notice to Users via any contact information available to the Owner. It is strongly recommended to check this page often, referring to the date of the last modification listed at the bottom. <br><br>Should the changes affect processing activities performed on the basis of the User's consent, the Owner shall collect new consent from the User, where required.</p>
|
||||
|
||||
<div class="mt-4">
|
||||
<h3 class="h4">Definitions and legal references</h3>
|
||||
<h4 class="h5">Personal Data (or Data)</h4>
|
||||
<p>Any information that directly, indirectly, or in connection with other information – including a personal identification number – allows for the identification or identifiability of a natural person.</p>
|
||||
|
||||
<h4 class="h5">Usage Data</h4>
|
||||
<p>Information collected automatically through this Application (or third-party services employed in this Application), which can include: the IP addresses or domain names of the computers utilized by the Users who use this Application, the URI addresses (Uniform Resource Identifier), the time of the request, the method utilized to submit the request to the server, the size of the file received in response, the numerical code indicating the status of the server's answer (successful outcome, error, etc.), the country of origin, the features of the browser and the operating system utilized by the User, the various time details per visit (e.g., the time spent on each page within the Application) and the details about the path followed within the Application with special reference to the sequence of pages visited, and other parameters about the device operating system and/or the User's IT environment.</p>
|
||||
|
||||
<h4 class="h5">User</h4>
|
||||
<p>The individual using this Application who, unless otherwise specified, coincides with the Data Subject.</p>
|
||||
|
||||
<h4 class="h5">Data Subject</h4>
|
||||
<p>The natural person to whom the Personal Data refers.</p>
|
||||
|
||||
<h4 class="h5">Data Processor (or Data Supervisor)</h4>
|
||||
<p>The natural or legal person, public authority, agency or other body which processes Personal Data on behalf of the Controller, as described in this privacy policy.</p>
|
||||
|
||||
<h4 class="h5">Data Controller (or Owner)</h4>
|
||||
<p>The natural or legal person, public authority, agency or other body which, alone or jointly with others, determines the purposes and means of the processing of Personal Data, including the security measures concerning the operation and use of this Application. The Data Controller, unless otherwise specified, is the Owner of this Application.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
23
templates/rate_limit.html
Normal file
@ -0,0 +1,23 @@
|
||||
{% extends "head.html" %}
|
||||
|
||||
{% block title %}Rate Limit Exceeded{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-lg py-4">
|
||||
<div class="p-5 mb-4 bg-light rounded-3">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-exclamation-triangle text-warning" style="font-size: 4rem;"></i>
|
||||
<h1 class="display-4 mt-3">Rate Limit Exceeded</h1>
|
||||
<p class="lead">
|
||||
We've noticed that you're using our service very frequently. Such frequent checks are typically associated with business usage, which contradicts our principles of providing free vehicle history checks for individual users.
|
||||
</p>
|
||||
<p class="lead">
|
||||
Please try again later or contact us if you need a business solution.
|
||||
</p>
|
||||
<div class="mt-4">
|
||||
<a href="/" class="btn btn-primary">Return to Homepage</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
178
templates/report_pdf.html
Normal file
@ -0,0 +1,178 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vehicle History Report</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 2px solid #333;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
.header h1 {
|
||||
font-size: 24px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.header p {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
.report-date {
|
||||
text-align: right;
|
||||
font-size: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.section {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 5px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
table, th, td {
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
th, td {
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
th {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 50px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
border-top: 1px solid #ddd;
|
||||
padding-top: 20px;
|
||||
}
|
||||
.disclaimer {
|
||||
font-size: 10px;
|
||||
margin-top: 20px;
|
||||
padding: 10px;
|
||||
background-color: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>Vehicle History Report</h1>
|
||||
<p>SALVAGEDB.COM - Comprehensive Vehicle History Check</p>
|
||||
</div>
|
||||
|
||||
<div class="report-date">
|
||||
<p>Report Date: {{report_date}}</p>
|
||||
<p>Report ID: {{report_id}}</p>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">Vehicle Information</h2>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>VIN</strong></td>
|
||||
<td>{{vin}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Make</strong></td>
|
||||
<td>{{det[0][1]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Model</strong></td>
|
||||
<td>{{det[0][2]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Year</strong></td>
|
||||
<td>{{det[0][3]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Body Style</strong></td>
|
||||
<td>{{det[0][4]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Engine</strong></td>
|
||||
<td>{{det[0][5]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Cylinders</strong></td>
|
||||
<td>{{det[0][6]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Drive</strong></td>
|
||||
<td>{{det[0][7]}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% if his %}
|
||||
<div class="section">
|
||||
<h2 class="section-title">Salvage History</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>VIN</th>
|
||||
<th>Title</th>
|
||||
<th>Odometer</th>
|
||||
<th>Odometer Status</th>
|
||||
<th>Primary Damage</th>
|
||||
<th>Secondary Damage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for it in his %}
|
||||
<tr>
|
||||
<td>{{it[1]}}</td>
|
||||
<td>{{it[2]}}</td>
|
||||
<td>{{it[3]}}</td>
|
||||
<td>{{it[4]}}</td>
|
||||
<td>{{it[5]}}</td>
|
||||
<td>{{it[6]}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="section">
|
||||
<div class="alert-warning" style="padding: 15px; background-color: #fcf8e3; border: 1px solid #faebcc; color: #8a6d3b;">
|
||||
<h2 style="font-size: 16px; margin: 0;">Salvage history not found.</h2>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="disclaimer">
|
||||
<p><strong>Disclaimer:</strong> This report provides information about salvage or junk vehicles; damage from hail, flood or fire; mileage discrepancies or odometer rollback; and gray market vehicles. We do not claim that the car got in our databank has salvage title, but the fact that it has been damaged for sure. Our site helps people avoid buying a damaged vehicle in the past.</p>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>© {{current_year}} SalvageDB.com - All Rights Reserved</p>
|
||||
<p>This report is provided as is without any guarantees or warranty. For additional assistance, please contact us.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
274
templates/report_printable.html
Normal file
@ -0,0 +1,274 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vehicle History Report - {{report_id}}</title>
|
||||
<style>
|
||||
@media print {
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.no-print {
|
||||
display: none !important;
|
||||
}
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 10mm;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 2px solid #333;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
.header h1 {
|
||||
font-size: 24px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.header p {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
.report-date {
|
||||
text-align: right;
|
||||
font-size: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.section {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 5px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
table, th, td {
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
th, td {
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
th {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 50px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
border-top: 1px solid #ddd;
|
||||
padding-top: 20px;
|
||||
}
|
||||
.disclaimer {
|
||||
font-size: 10px;
|
||||
margin-top: 20px;
|
||||
padding: 10px;
|
||||
background-color: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
.print-controls {
|
||||
background-color: #f5f5f5;
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.btn {
|
||||
display: inline-block;
|
||||
font-weight: 400;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
user-select: none;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.375rem 0.75rem;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
border-radius: 0.25rem;
|
||||
text-decoration: none;
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background-color: #007bff;
|
||||
border-color: #007bff;
|
||||
}
|
||||
.btn-secondary {
|
||||
color: #fff;
|
||||
background-color: #6c757d;
|
||||
border-color: #6c757d;
|
||||
}
|
||||
.error-message {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
padding: 10px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
.error-details {
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
background: #f8f9fa;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
max-height: 200px;
|
||||
overflow: auto;
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function printReport() {
|
||||
window.print();
|
||||
}
|
||||
|
||||
function generatePDF() {
|
||||
// Уведомляем пользователя, что используем встроенную функцию браузера
|
||||
alert("Используйте функцию 'Сохранить как PDF' в диалоговом окне печати вашего браузера.");
|
||||
window.print();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="print-controls no-print">
|
||||
<button class="btn btn-primary" onclick="printReport()">Print Report</button>
|
||||
<button class="btn btn-secondary" onclick="generatePDF()">Save as PDF</button>
|
||||
<a class="btn btn-secondary" href="javascript:history.back()">Back to Vehicle Details</a>
|
||||
</div>
|
||||
|
||||
{% if error_message %}
|
||||
<div class="error-message no-print">
|
||||
<h3>PDF Generation Error</h3>
|
||||
<p>The system encountered an error while generating the PDF. You can print this page or save it as PDF using your browser's print function.</p>
|
||||
<div class="error-details">
|
||||
{{ error_message }}
|
||||
</div>
|
||||
<p>Note: To install WeasyPrint dependencies on Windows, see the <a href="https://doc.courtbouillon.org/weasyprint/stable/first_steps.html#windows" target="_blank">installation instructions</a>.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="header">
|
||||
<h1>Vehicle History Report</h1>
|
||||
<p>SALVAGEDB.COM - Comprehensive Vehicle History Check</p>
|
||||
</div>
|
||||
|
||||
<div class="report-date">
|
||||
<p>Report Date: {{report_date}}</p>
|
||||
<p>Report ID: {{report_id}}</p>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">Vehicle Information</h2>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>VIN</strong></td>
|
||||
<td>{{vin}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Make</strong></td>
|
||||
<td>{{det[0][1]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Model</strong></td>
|
||||
<td>{{det[0][2]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Year</strong></td>
|
||||
<td>{{det[0][3]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Body Style</strong></td>
|
||||
<td>{{det[0][4]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Engine</strong></td>
|
||||
<td>{{det[0][5]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Cylinders</strong></td>
|
||||
<td>{{det[0][6]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Drive</strong></td>
|
||||
<td>{{det[0][7]}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% if his %}
|
||||
<div class="section">
|
||||
<h2 class="section-title">Salvage History</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>VIN</th>
|
||||
<th>Title</th>
|
||||
<th>Odometer</th>
|
||||
<th>Odometer Status</th>
|
||||
<th>Primary Damage</th>
|
||||
<th>Secondary Damage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for it in his %}
|
||||
<tr>
|
||||
<td>{{it[1]}}</td>
|
||||
<td>{{it[2]}}</td>
|
||||
<td>{{it[3]}}</td>
|
||||
<td>{{it[4]}}</td>
|
||||
<td>{{it[5]}}</td>
|
||||
<td>{{it[6]}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="section">
|
||||
<div class="alert-warning" style="padding: 15px; background-color: #fcf8e3; border: 1px solid #faebcc; color: #8a6d3b;">
|
||||
<h2 style="font-size: 16px; margin: 0;">Salvage history not found.</h2>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="disclaimer">
|
||||
<p><strong>Disclaimer:</strong> This report provides information about salvage or junk vehicles; damage from hail, flood or fire; mileage discrepancies or odometer rollback; and gray market vehicles. We do not claim that the car got in our databank has salvage title, but the fact that it has been damaged for sure. Our site helps people avoid buying a damaged vehicle in the past.</p>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>© {{current_year}} SalvageDB.com - All Rights Reserved</p>
|
||||
<p>This report is provided as is without any guarantees or warranty. For additional assistance, please contact us.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,11 +1,13 @@
|
||||
{% extends "head.html" %}
|
||||
|
||||
{% block title %}Vehicle History Check{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="hero-unit">
|
||||
<h1>Buying a Used Car? Check it!</h1>
|
||||
<br>
|
||||
<p><a href="https://www.salvagedb.com/" title="Salvagedb.com" rel="nofollow">Salvagedb.com</a> provides
|
||||
<div class="container-lg py-4">
|
||||
<div class="p-5 mb-4 bg-light rounded-3">
|
||||
<h1 class="display-4">Buying a Used Car? Check it!</h1>
|
||||
<p class="lead">
|
||||
<a href="https://www.salvagedb.com/" title="Salvagedb.com" rel="nofollow">Salvagedb.com</a> provides
|
||||
information about salvage or junk vehicles; damage from hail, flood or fire; mileage discrepancies or
|
||||
odometer rollback; and gray market vehicles. We do not claim that the car got in our databank has salvage
|
||||
title, but the fact that it has been damaged for sure. Our site helps people avoid buying a damaged vehicle
|
||||
@ -13,9 +15,14 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="details-container">
|
||||
<div class="car-details">
|
||||
<table id="one-column-emphasis">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2 class="h5 mb-0">Vehicle Details</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-sm">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Make</td>
|
||||
@ -48,11 +55,19 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
{% if his %}
|
||||
<div class="history-container">
|
||||
<table id="hor-minimalist-a">
|
||||
<thead>
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2 class="h5 mb-0">Salvage History</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th scope="col">VIN</th>
|
||||
<th scope="col">Title</th>
|
||||
@ -76,11 +91,21 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- PDF Report Download Button -->
|
||||
<div class="mt-3">
|
||||
<a href="/salvagereport/{{vin}}" class="btn btn-primary w-100">
|
||||
<i class="fas fa-file-pdf me-2"></i> Download PDF Report
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="not-found">
|
||||
<h2>Salvage history not found.</h2>
|
||||
<div class="alert alert-warning">
|
||||
<h2 class="h5 mb-0">Salvage history not found.</h2>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||