diff --git a/lib/constants.js b/lib/constants.js index d261af04..c6e97e2e 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -1 +1,335 @@ export const AUTH_COOKIE_NAME = 'umami.auth'; + +export const DESKTOP_SCREEN_WIDTH = 1920; +export const MOBILE_SCREEN_WIDTH = 479; + +export const OPERATING_SYSTEMS = [ + 'iOS', + 'Android OS', + 'BlackBerry OS', + 'Windows Mobile', + 'Amazon OS', + 'Windows 3.11', + 'Windows 95', + 'Windows 98', + 'Windows 2000', + 'Windows XP', + 'Windows Server 2003', + 'Windows Vista', + 'Windows 7', + 'Windows 8', + 'Windows 8.1', + 'Windows 10', + 'Windows ME', + 'Open BSD', + 'Sun OS', + 'Linux', + 'Mac OS', + 'QNX', + 'BeOS', + 'OS/2', + 'Chrome OS', +]; + +export const DESKTOP_OS = [ + 'Windows 3.11', + 'Windows 95', + 'Windows 98', + 'Windows 2000', + 'Windows XP', + 'Windows Server 2003', + 'Windows Vista', + 'Windows 7', + 'Windows 8', + 'Windows 8.1', + 'Windows 10', + 'Windows ME', + 'Open BSD', + 'Sun OS', + 'Linux', + 'Mac OS', + 'QNX', + 'BeOS', + 'OS/2', + 'Chrome OS', +]; + +export const MOBILE_OS = ['iOS', 'Android OS', 'BlackBerry OS', 'Windows Mobile', 'Amazon OS']; + +export const BROWSERS = { + aol: 'AOL', + edge: 'Edge', + 'edge-ios': 'Edge (iOS)', + yandexbrowser: 'Yandex', + kakaotalk: 'KKaoTalk', + samsung: 'Samsung', + silk: 'Silk', + miui: 'MIUI', + beaker: 'Beaker', + 'edge-chromium': 'Edge (Chromium)', + chrome: 'Chrome', + 'chromium-webview': 'Chrome (webview)', + phantomjs: 'PhantomJS', + crios: 'Chrome (iOS)', + firefox: 'Firefox', + fxios: 'Firefox (iOS)', + 'opera-mini': 'Opera Mini', + opera: 'Opera', + ie: 'IE', + bb10: 'BlackBerry 10', + android: 'Android', + ios: 'iOS', + safari: 'Safari', + facebook: 'Facebook', + instagram: 'Instagram', + 'ios-webview': 'iOS (webview)', + searchbot: 'Searchbot', +}; + +export const ISO_COUNTRIES = { + AF: 'Afghanistan', + AX: 'Aland Islands', + AL: 'Albania', + DZ: 'Algeria', + AS: 'American Samoa', + AD: 'Andorra', + AO: 'Angola', + AI: 'Anguilla', + AQ: 'Antarctica', + AG: 'Antigua And Barbuda', + AR: 'Argentina', + AM: 'Armenia', + AW: 'Aruba', + AU: 'Australia', + AT: 'Austria', + AZ: 'Azerbaijan', + BS: 'Bahamas', + BH: 'Bahrain', + BD: 'Bangladesh', + BB: 'Barbados', + BY: 'Belarus', + BE: 'Belgium', + BZ: 'Belize', + BJ: 'Benin', + BM: 'Bermuda', + BT: 'Bhutan', + BO: 'Bolivia', + BA: 'Bosnia And Herzegovina', + BW: 'Botswana', + BV: 'Bouvet Island', + BR: 'Brazil', + IO: 'British Indian Ocean Territory', + BN: 'Brunei Darussalam', + BG: 'Bulgaria', + BF: 'Burkina Faso', + BI: 'Burundi', + KH: 'Cambodia', + CM: 'Cameroon', + CA: 'Canada', + CV: 'Cape Verde', + KY: 'Cayman Islands', + CF: 'Central African Republic', + TD: 'Chad', + CL: 'Chile', + CN: 'China', + CX: 'Christmas Island', + CC: 'Cocos (Keeling) Islands', + CO: 'Colombia', + KM: 'Comoros', + CG: 'Congo', + CD: 'Congo, Democratic Republic', + CK: 'Cook Islands', + CR: 'Costa Rica', + CI: "Cote D'Ivoire", + HR: 'Croatia', + CU: 'Cuba', + CY: 'Cyprus', + CZ: 'Czech Republic', + DK: 'Denmark', + DJ: 'Djibouti', + DM: 'Dominica', + DO: 'Dominican Republic', + EC: 'Ecuador', + EG: 'Egypt', + SV: 'El Salvador', + GQ: 'Equatorial Guinea', + ER: 'Eritrea', + EE: 'Estonia', + ET: 'Ethiopia', + FK: 'Falkland Islands (Malvinas)', + FO: 'Faroe Islands', + FJ: 'Fiji', + FI: 'Finland', + FR: 'France', + GF: 'French Guiana', + PF: 'French Polynesia', + TF: 'French Southern Territories', + GA: 'Gabon', + GM: 'Gambia', + GE: 'Georgia', + DE: 'Germany', + GH: 'Ghana', + GI: 'Gibraltar', + GR: 'Greece', + GL: 'Greenland', + GD: 'Grenada', + GP: 'Guadeloupe', + GU: 'Guam', + GT: 'Guatemala', + GG: 'Guernsey', + GN: 'Guinea', + GW: 'Guinea-Bissau', + GY: 'Guyana', + HT: 'Haiti', + HM: 'Heard Island & Mcdonald Islands', + VA: 'Holy See (Vatican City State)', + HN: 'Honduras', + HK: 'Hong Kong', + HU: 'Hungary', + IS: 'Iceland', + IN: 'India', + ID: 'Indonesia', + IR: 'Iran, Islamic Republic Of', + IQ: 'Iraq', + IE: 'Ireland', + IM: 'Isle Of Man', + IL: 'Israel', + IT: 'Italy', + JM: 'Jamaica', + JP: 'Japan', + JE: 'Jersey', + JO: 'Jordan', + KZ: 'Kazakhstan', + KE: 'Kenya', + KI: 'Kiribati', + KR: 'Korea', + KW: 'Kuwait', + KG: 'Kyrgyzstan', + LA: "Lao People's Democratic Republic", + LV: 'Latvia', + LB: 'Lebanon', + LS: 'Lesotho', + LR: 'Liberia', + LY: 'Libyan Arab Jamahiriya', + LI: 'Liechtenstein', + LT: 'Lithuania', + LU: 'Luxembourg', + MO: 'Macao', + MK: 'Macedonia', + MG: 'Madagascar', + MW: 'Malawi', + MY: 'Malaysia', + MV: 'Maldives', + ML: 'Mali', + MT: 'Malta', + MH: 'Marshall Islands', + MQ: 'Martinique', + MR: 'Mauritania', + MU: 'Mauritius', + YT: 'Mayotte', + MX: 'Mexico', + FM: 'Micronesia, Federated States Of', + MD: 'Moldova', + MC: 'Monaco', + MN: 'Mongolia', + ME: 'Montenegro', + MS: 'Montserrat', + MA: 'Morocco', + MZ: 'Mozambique', + MM: 'Myanmar', + NA: 'Namibia', + NR: 'Nauru', + NP: 'Nepal', + NL: 'Netherlands', + AN: 'Netherlands Antilles', + NC: 'New Caledonia', + NZ: 'New Zealand', + NI: 'Nicaragua', + NE: 'Niger', + NG: 'Nigeria', + NU: 'Niue', + NF: 'Norfolk Island', + MP: 'Northern Mariana Islands', + NO: 'Norway', + OM: 'Oman', + PK: 'Pakistan', + PW: 'Palau', + PS: 'Palestinian Territory, Occupied', + PA: 'Panama', + PG: 'Papua New Guinea', + PY: 'Paraguay', + PE: 'Peru', + PH: 'Philippines', + PN: 'Pitcairn', + PL: 'Poland', + PT: 'Portugal', + PR: 'Puerto Rico', + QA: 'Qatar', + RE: 'Reunion', + RO: 'Romania', + RU: 'Russia', + RW: 'Rwanda', + BL: 'Saint Barthelemy', + SH: 'Saint Helena', + KN: 'Saint Kitts And Nevis', + LC: 'Saint Lucia', + MF: 'Saint Martin', + PM: 'Saint Pierre And Miquelon', + VC: 'Saint Vincent And Grenadines', + WS: 'Samoa', + SM: 'San Marino', + ST: 'Sao Tome And Principe', + SA: 'Saudi Arabia', + SN: 'Senegal', + RS: 'Serbia', + SC: 'Seychelles', + SL: 'Sierra Leone', + SG: 'Singapore', + SK: 'Slovakia', + SI: 'Slovenia', + SB: 'Solomon Islands', + SO: 'Somalia', + ZA: 'South Africa', + GS: 'South Georgia And Sandwich Isl.', + ES: 'Spain', + LK: 'Sri Lanka', + SD: 'Sudan', + SR: 'Suriname', + SJ: 'Svalbard And Jan Mayen', + SZ: 'Swaziland', + SE: 'Sweden', + CH: 'Switzerland', + SY: 'Syrian Arab Republic', + TW: 'Taiwan', + TJ: 'Tajikistan', + TZ: 'Tanzania', + TH: 'Thailand', + TL: 'Timor-Leste', + TG: 'Togo', + TK: 'Tokelau', + TO: 'Tonga', + TT: 'Trinidad And Tobago', + TN: 'Tunisia', + TR: 'Turkey', + TM: 'Turkmenistan', + TC: 'Turks And Caicos Islands', + TV: 'Tuvalu', + UG: 'Uganda', + UA: 'Ukraine', + AE: 'United Arab Emirates', + GB: 'United Kingdom', + US: 'United States', + UM: 'United States Outlying Islands', + UY: 'Uruguay', + UZ: 'Uzbekistan', + VU: 'Vanuatu', + VE: 'Venezuela', + VN: 'Viet Nam', + VG: 'Virgin Islands, British', + VI: 'Virgin Islands, U.S.', + WF: 'Wallis And Futuna', + EH: 'Western Sahara', + YE: 'Yemen', + ZM: 'Zambia', + ZW: 'Zimbabwe', +}; diff --git a/lib/filters.js b/lib/filters.js index d83be5e7..a6a3dbad 100644 --- a/lib/filters.js +++ b/lib/filters.js @@ -1,283 +1,7 @@ -const browsers = { - aol: 'AOL', - edge: 'Edge', - 'edge-ios': 'Edge (iOS)', - yandexbrowser: 'Yandex', - kakaotalk: 'KKaoTalk', - samsung: 'Samsung', - silk: 'Silk', - miui: 'MIUI', - beaker: 'Beaker', - 'edge-chromium': 'Edge (Chromium)', - chrome: 'Chrome', - 'chromium-webview': 'Chrome (webview)', - phantomjs: 'PhantomJS', - crios: 'Chrome (iOS)', - firefox: 'Firefox', - fxios: 'Firefox (iOS)', - 'opera-mini': 'Opera Mini', - opera: 'Opera', - ie: 'IE', - bb10: 'BlackBerry 10', - android: 'Android', - ios: 'iOS', - safari: 'Safari', - facebook: 'Facebook', - instagram: 'Instagram', - 'ios-webview': 'iOS (webview)', - searchbot: 'Searchbot', -}; - -const isoCountries = { - AF: 'Afghanistan', - AX: 'Aland Islands', - AL: 'Albania', - DZ: 'Algeria', - AS: 'American Samoa', - AD: 'Andorra', - AO: 'Angola', - AI: 'Anguilla', - AQ: 'Antarctica', - AG: 'Antigua And Barbuda', - AR: 'Argentina', - AM: 'Armenia', - AW: 'Aruba', - AU: 'Australia', - AT: 'Austria', - AZ: 'Azerbaijan', - BS: 'Bahamas', - BH: 'Bahrain', - BD: 'Bangladesh', - BB: 'Barbados', - BY: 'Belarus', - BE: 'Belgium', - BZ: 'Belize', - BJ: 'Benin', - BM: 'Bermuda', - BT: 'Bhutan', - BO: 'Bolivia', - BA: 'Bosnia And Herzegovina', - BW: 'Botswana', - BV: 'Bouvet Island', - BR: 'Brazil', - IO: 'British Indian Ocean Territory', - BN: 'Brunei Darussalam', - BG: 'Bulgaria', - BF: 'Burkina Faso', - BI: 'Burundi', - KH: 'Cambodia', - CM: 'Cameroon', - CA: 'Canada', - CV: 'Cape Verde', - KY: 'Cayman Islands', - CF: 'Central African Republic', - TD: 'Chad', - CL: 'Chile', - CN: 'China', - CX: 'Christmas Island', - CC: 'Cocos (Keeling) Islands', - CO: 'Colombia', - KM: 'Comoros', - CG: 'Congo', - CD: 'Congo, Democratic Republic', - CK: 'Cook Islands', - CR: 'Costa Rica', - CI: "Cote D'Ivoire", - HR: 'Croatia', - CU: 'Cuba', - CY: 'Cyprus', - CZ: 'Czech Republic', - DK: 'Denmark', - DJ: 'Djibouti', - DM: 'Dominica', - DO: 'Dominican Republic', - EC: 'Ecuador', - EG: 'Egypt', - SV: 'El Salvador', - GQ: 'Equatorial Guinea', - ER: 'Eritrea', - EE: 'Estonia', - ET: 'Ethiopia', - FK: 'Falkland Islands (Malvinas)', - FO: 'Faroe Islands', - FJ: 'Fiji', - FI: 'Finland', - FR: 'France', - GF: 'French Guiana', - PF: 'French Polynesia', - TF: 'French Southern Territories', - GA: 'Gabon', - GM: 'Gambia', - GE: 'Georgia', - DE: 'Germany', - GH: 'Ghana', - GI: 'Gibraltar', - GR: 'Greece', - GL: 'Greenland', - GD: 'Grenada', - GP: 'Guadeloupe', - GU: 'Guam', - GT: 'Guatemala', - GG: 'Guernsey', - GN: 'Guinea', - GW: 'Guinea-Bissau', - GY: 'Guyana', - HT: 'Haiti', - HM: 'Heard Island & Mcdonald Islands', - VA: 'Holy See (Vatican City State)', - HN: 'Honduras', - HK: 'Hong Kong', - HU: 'Hungary', - IS: 'Iceland', - IN: 'India', - ID: 'Indonesia', - IR: 'Iran, Islamic Republic Of', - IQ: 'Iraq', - IE: 'Ireland', - IM: 'Isle Of Man', - IL: 'Israel', - IT: 'Italy', - JM: 'Jamaica', - JP: 'Japan', - JE: 'Jersey', - JO: 'Jordan', - KZ: 'Kazakhstan', - KE: 'Kenya', - KI: 'Kiribati', - KR: 'Korea', - KW: 'Kuwait', - KG: 'Kyrgyzstan', - LA: "Lao People's Democratic Republic", - LV: 'Latvia', - LB: 'Lebanon', - LS: 'Lesotho', - LR: 'Liberia', - LY: 'Libyan Arab Jamahiriya', - LI: 'Liechtenstein', - LT: 'Lithuania', - LU: 'Luxembourg', - MO: 'Macao', - MK: 'Macedonia', - MG: 'Madagascar', - MW: 'Malawi', - MY: 'Malaysia', - MV: 'Maldives', - ML: 'Mali', - MT: 'Malta', - MH: 'Marshall Islands', - MQ: 'Martinique', - MR: 'Mauritania', - MU: 'Mauritius', - YT: 'Mayotte', - MX: 'Mexico', - FM: 'Micronesia, Federated States Of', - MD: 'Moldova', - MC: 'Monaco', - MN: 'Mongolia', - ME: 'Montenegro', - MS: 'Montserrat', - MA: 'Morocco', - MZ: 'Mozambique', - MM: 'Myanmar', - NA: 'Namibia', - NR: 'Nauru', - NP: 'Nepal', - NL: 'Netherlands', - AN: 'Netherlands Antilles', - NC: 'New Caledonia', - NZ: 'New Zealand', - NI: 'Nicaragua', - NE: 'Niger', - NG: 'Nigeria', - NU: 'Niue', - NF: 'Norfolk Island', - MP: 'Northern Mariana Islands', - NO: 'Norway', - OM: 'Oman', - PK: 'Pakistan', - PW: 'Palau', - PS: 'Palestinian Territory, Occupied', - PA: 'Panama', - PG: 'Papua New Guinea', - PY: 'Paraguay', - PE: 'Peru', - PH: 'Philippines', - PN: 'Pitcairn', - PL: 'Poland', - PT: 'Portugal', - PR: 'Puerto Rico', - QA: 'Qatar', - RE: 'Reunion', - RO: 'Romania', - RU: 'Russia', - RW: 'Rwanda', - BL: 'Saint Barthelemy', - SH: 'Saint Helena', - KN: 'Saint Kitts And Nevis', - LC: 'Saint Lucia', - MF: 'Saint Martin', - PM: 'Saint Pierre And Miquelon', - VC: 'Saint Vincent And Grenadines', - WS: 'Samoa', - SM: 'San Marino', - ST: 'Sao Tome And Principe', - SA: 'Saudi Arabia', - SN: 'Senegal', - RS: 'Serbia', - SC: 'Seychelles', - SL: 'Sierra Leone', - SG: 'Singapore', - SK: 'Slovakia', - SI: 'Slovenia', - SB: 'Solomon Islands', - SO: 'Somalia', - ZA: 'South Africa', - GS: 'South Georgia And Sandwich Isl.', - ES: 'Spain', - LK: 'Sri Lanka', - SD: 'Sudan', - SR: 'Suriname', - SJ: 'Svalbard And Jan Mayen', - SZ: 'Swaziland', - SE: 'Sweden', - CH: 'Switzerland', - SY: 'Syrian Arab Republic', - TW: 'Taiwan', - TJ: 'Tajikistan', - TZ: 'Tanzania', - TH: 'Thailand', - TL: 'Timor-Leste', - TG: 'Togo', - TK: 'Tokelau', - TO: 'Tonga', - TT: 'Trinidad And Tobago', - TN: 'Tunisia', - TR: 'Turkey', - TM: 'Turkmenistan', - TC: 'Turks And Caicos Islands', - TV: 'Tuvalu', - UG: 'Uganda', - UA: 'Ukraine', - AE: 'United Arab Emirates', - GB: 'United Kingdom', - US: 'United States', - UM: 'United States Outlying Islands', - UY: 'Uruguay', - UZ: 'Uzbekistan', - VU: 'Vanuatu', - VE: 'Venezuela', - VN: 'Viet Nam', - VG: 'Virgin Islands, British', - VI: 'Virgin Islands, U.S.', - WF: 'Wallis And Futuna', - EH: 'Western Sahara', - YE: 'Yemen', - ZM: 'Zambia', - ZW: 'Zimbabwe', -}; +import { BROWSERS, ISO_COUNTRIES } from './constants'; export const browserFilter = data => - data.map(({ x, ...props }) => ({ x: browsers[x] || x, ...props })); + data.map(({ x, ...props }) => ({ x: BROWSERS[x] || x, ...props })); export const urlFilter = data => data.filter(({ x }) => x !== '' && !x.startsWith('#')); @@ -308,7 +32,7 @@ export const deviceFilter = data => { }; export const countryFilter = data => - data.map(({ x, ...props }) => ({ x: isoCountries[x] || x, ...props })); + data.map(({ x, ...props }) => ({ x: ISO_COUNTRIES[x] || x, ...props })); export const percentFilter = data => { const total = data.reduce((n, { y }) => n + y, 0); diff --git a/lib/request.js b/lib/request.js index b92cda53..f2b70c54 100644 --- a/lib/request.js +++ b/lib/request.js @@ -3,6 +3,7 @@ import { browserName, detectOS } from 'detect-browser'; import isLocalhost from 'is-localhost-ip'; import maxmind from 'maxmind'; import geolite2 from 'geolite2-redist'; +import { DESKTOP_OS, MOBILE_OS, DESKTOP_SCREEN_WIDTH, MOBILE_SCREEN_WIDTH } from './constants'; export function getIpAddress(req) { // Cloudflare @@ -13,12 +14,20 @@ export function getIpAddress(req) { return requestIp.getClientIp(req); } -export function getDevice(req) { - const userAgent = req.headers['user-agent']; - const browser = browserName(userAgent); - const os = detectOS(userAgent); +export function getDevice(screen, browser, os) { + const [width] = screen.split('x'); - return { userAgent, browser, os }; + if (DESKTOP_OS.includes(os)) { + if (os === 'Chrome OS' || width < DESKTOP_SCREEN_WIDTH) { + return 'laptop'; + } + return 'desktop'; + } else if (MOBILE_OS.includes(os)) { + if (os === 'Amazon OS' || width < MOBILE_SCREEN_WIDTH) { + return 'tablet'; + } + return 'mobile'; + } } export async function getCountry(req, ip) { @@ -43,3 +52,14 @@ export async function getCountry(req, ip) { return result.country.iso_code; } + +export async function getClientInfo(req, { screen }) { + const ip = getIpAddress(req); + const country = await getCountry(req, ip); + const userAgent = req.headers['user-agent']; + const browser = browserName(userAgent); + const os = detectOS(userAgent); + const device = getDevice(screen, browser, os); + + return { userAgent, browser, os, ip, country, device }; +} diff --git a/lib/session.js b/lib/session.js index 932c6a8b..ca8bb697 100644 --- a/lib/session.js +++ b/lib/session.js @@ -1,5 +1,5 @@ import { getWebsite, getSession, createSession } from 'lib/db'; -import { getCountry, getDevice, getIpAddress } from 'lib/request'; +import { getClientInfo } from 'lib/request'; import { uuid, isValidId, verifyToken } from 'lib/crypto'; export async function verifySession(req) { @@ -13,9 +13,7 @@ export async function verifySession(req) { try { return await verifyToken(session); } catch { - const ip = getIpAddress(req); - const { userAgent, browser, os } = getDevice(req); - const country = await getCountry(req, ip); + const { userAgent, browser, os, ip, country, device } = await getClientInfo(req, payload); if (website_uuid) { const website = await getWebsite({ website_uuid }); @@ -35,6 +33,7 @@ export async function verifySession(req) { screen, language, country, + device, }); } diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5a0a74b7..fdf9505e 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -8,23 +8,23 @@ datasource db { } model account { - created_at DateTime? @default(now()) - is_admin Boolean @default(false) - password String - updated_at DateTime? @default(now()) user_id Int @default(autoincrement()) @id username String @unique + password String + is_admin Boolean @default(false) + created_at DateTime? @default(now()) + updated_at DateTime? @default(now()) website website[] } model event { - created_at DateTime? @default(now()) event_id Int @default(autoincrement()) @id + website_id Int + session_id Int + created_at DateTime? @default(now()) + url String event_type String event_value String - session_id Int - url String - website_id Int session session @relation(fields: [session_id], references: [session_id]) website website @relation(fields: [website_id], references: [website_id]) @@ -34,12 +34,12 @@ model event { } model pageview { - created_at DateTime? @default(now()) - referrer String? - session_id Int - url String view_id Int @default(autoincrement()) @id website_id Int + session_id Int + created_at DateTime? @default(now()) + url String + referrer String? session session @relation(fields: [session_id], references: [session_id]) website website @relation(fields: [website_id], references: [website_id]) @@ -49,16 +49,17 @@ model pageview { } model session { - browser String? - country String? - created_at DateTime? @default(now()) - hostname String? - language String? - os String? - screen String? session_id Int @default(autoincrement()) @id session_uuid String @unique website_id Int + created_at DateTime? @default(now()) + hostname String? + browser String? + os String? + screen String? + language String? + country String? + device String? website website @relation(fields: [website_id], references: [website_id]) event event[] pageview pageview[] @@ -68,11 +69,11 @@ model session { } model website { - created_at DateTime? @default(now()) - label String - user_id Int website_id Int @default(autoincrement()) @id website_uuid String @unique + label String + created_at DateTime? @default(now()) + user_id Int account account @relation(fields: [user_id], references: [user_id]) event event[] pageview pageview[] diff --git a/sql/schema.postgresql.sql b/sql/schema.postgresql.sql index 6f43e90b..c237de3a 100644 --- a/sql/schema.postgresql.sql +++ b/sql/schema.postgresql.sql @@ -23,6 +23,7 @@ create table session ( hostname varchar(100), browser varchar(20), os varchar(20), + device varchar(20), screen varchar(11), language varchar(35), country char(2)