@tiledesk/tiledesk-server 2.18.4 → 2.18.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,101 @@
1
+ 'use strict';
2
+
3
+ var moment = require('moment-timezone');
4
+ var Project = require('../models/project');
5
+
6
+ /**
7
+ * @param {string} tz
8
+ * @returns {string|null}
9
+ */
10
+ function normalizeIanaTimezone(tz) {
11
+ if (!tz || typeof tz !== 'string') {
12
+ return null;
13
+ }
14
+ var trimmed = tz.trim();
15
+ if (!trimmed || !moment.tz.zone(trimmed)) {
16
+ return null;
17
+ }
18
+ return trimmed;
19
+ }
20
+
21
+ function timezoneFromOperatingHours(operatingHours) {
22
+ if (operatingHours == null || operatingHours === '') {
23
+ return null;
24
+ }
25
+ var parsed = operatingHours;
26
+ if (typeof operatingHours === 'string') {
27
+ try {
28
+ parsed = JSON.parse(operatingHours);
29
+ } catch (e) {
30
+ return null;
31
+ }
32
+ }
33
+ if (!parsed || typeof parsed !== 'object') {
34
+ return null;
35
+ }
36
+ return normalizeIanaTimezone(parsed.tzname);
37
+ }
38
+
39
+ /**
40
+ * Resolves IANA timezone for transcript exports (CSV, HTML, PDF, TXT).
41
+ * Priority: query ?tz= or ?timezone= → TRANSCRIPT_DISPLAY_TIMEZONE → project operatingHours.tzname → UTC.
42
+ *
43
+ * @param {import('express').Request} req
44
+ * @param {string} [idProject] — from message.id_project
45
+ * @returns {Promise<string>}
46
+ */
47
+ function resolveTranscriptTimezone(req, idProject) {
48
+ var fromQuery = normalizeIanaTimezone((req.query && (req.query.tz || req.query.timezone)) || '');
49
+ if (fromQuery) {
50
+ return Promise.resolve(fromQuery);
51
+ }
52
+
53
+ var fromEnv = normalizeIanaTimezone(process.env.TRANSCRIPT_DISPLAY_TIMEZONE || '');
54
+ if (fromEnv) {
55
+ return Promise.resolve(fromEnv);
56
+ }
57
+
58
+ if (!idProject) {
59
+ return Promise.resolve('UTC');
60
+ }
61
+
62
+ return Project.findOne({ _id: idProject })
63
+ .select('operatingHours activeOperatingHours')
64
+ .lean()
65
+ .exec()
66
+ .then(function (project) {
67
+ if (!project) {
68
+ return 'UTC';
69
+ }
70
+ var tz = timezoneFromOperatingHours(project.operatingHours);
71
+ return tz || 'UTC';
72
+ })
73
+ .catch(function () {
74
+ return 'UTC';
75
+ });
76
+ }
77
+
78
+ /**
79
+ * @param {Date|string|number} createdAt — instant in UTC (Mongo / JS Date)
80
+ * @param {string} timeZone — IANA, e.g. Europe/Rome, or UTC
81
+ * @returns {string}
82
+ */
83
+ function formatTranscriptInstant(createdAt, timeZone) {
84
+ if (createdAt == null) {
85
+ return '';
86
+ }
87
+ var mUtc = moment.utc(createdAt);
88
+ if (!mUtc.isValid()) {
89
+ return '';
90
+ }
91
+ if (!timeZone || timeZone === 'UTC') {
92
+ return mUtc.format('YYYY-MM-DD HH:mm:ss') + ' UTC';
93
+ }
94
+ return mUtc.tz(timeZone).format('YYYY-MM-DD HH:mm:ss z');
95
+ }
96
+
97
+ module.exports = {
98
+ resolveTranscriptTimezone: resolveTranscriptTimezone,
99
+ formatTranscriptInstant: formatTranscriptInstant,
100
+ normalizeIanaTimezone: normalizeIanaTimezone
101
+ };
@@ -0,0 +1,130 @@
1
+ doctype html
2
+ html(lang="en")
3
+ head
4
+ meta(charset="utf-8")
5
+ meta(name="viewport", content="width=device-width, initial-scale=1")
6
+ style(type="text/css").
7
+ :root {
8
+ --td-bg: #f4f6f8;
9
+ --td-surface: #ffffff;
10
+ --td-border: #e2e8f0;
11
+ --td-text: #0f172a;
12
+ --td-muted: #64748b;
13
+ --td-accent: #0d9488;
14
+ --td-accent-soft: rgba(13, 148, 136, 0.08);
15
+ --td-radius: 12px;
16
+ --td-shadow: 0 1px 3px rgba(15, 23, 42, 0.06), 0 8px 24px rgba(15, 23, 42, 0.06);
17
+ --td-font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
18
+ }
19
+ *, *::before, *::after { box-sizing: border-box; }
20
+ body {
21
+ margin: 0;
22
+ min-height: 100vh;
23
+ font-family: var(--td-font);
24
+ font-size: 16px;
25
+ line-height: 1.55;
26
+ color: var(--td-text);
27
+ background: var(--td-bg);
28
+ -webkit-font-smoothing: antialiased;
29
+ }
30
+ input { width: 80%; }
31
+ a {
32
+ color: var(--td-accent);
33
+ text-decoration: none;
34
+ font-weight: 600;
35
+ }
36
+ a:hover { text-decoration: underline; }
37
+
38
+ .transcript-page {
39
+ max-width: 720px;
40
+ margin: 0 auto;
41
+ padding: clamp(1rem, 4vw, 2rem) clamp(0.75rem, 3vw, 1.25rem) 2rem;
42
+ }
43
+ .transcript-header {
44
+ /* margin-bottom: 1.5rem; */
45
+ }
46
+ .transcript-brand-logo {
47
+ width: min(200px, 55vw);
48
+ height: auto;
49
+ /* margin: 0 auto 1rem; */
50
+ object-fit: contain;
51
+ }
52
+ .transcript-title-container {
53
+ text-align: center;
54
+ margin-bottom: 1.5rem;
55
+ }
56
+ .transcript-title {
57
+ margin: 0;
58
+ font-size: clamp(1.25rem, 4vw, 1.5rem);
59
+ font-weight: 700;
60
+ letter-spacing: -0.02em;
61
+ color: var(--td-text);
62
+ }
63
+ .transcript-wrapper {
64
+ background: var(--td-surface);
65
+ border: 1px solid var(--td-border);
66
+ border-radius: var(--td-radius);
67
+ box-shadow: var(--td-shadow);
68
+ padding: clamp(0.75rem, 2.5vw, 1.25rem);
69
+ }
70
+ .transcript-thread {
71
+ list-style: none;
72
+ margin: 0;
73
+ padding: 0;
74
+ display: flex;
75
+ flex-direction: column;
76
+ gap: 0.75rem;
77
+ }
78
+ .transcript-msg {
79
+ margin: 0;
80
+ padding: 0.875rem 1rem;
81
+ padding-bottom: 0.1rem;
82
+ border-radius: 10px;
83
+ background: var(--td-accent-soft);
84
+ border: 1px solid rgba(226, 232, 240, 0.9);
85
+ }
86
+ .transcript-msg:nth-child(even) {
87
+ background: #f8fafc;
88
+ border-color: var(--td-border);
89
+ }
90
+ .transcript-msg-meta {
91
+ display: flex;
92
+ flex-wrap: wrap;
93
+ align-items: baseline;
94
+ justify-content: flex-end;
95
+ gap: 0.35rem 0.75rem;
96
+ margin-top: 0.1rem;
97
+ margin-bottom: 0.4rem;
98
+ font-size: 0.8125rem;
99
+ color: var(--td-muted);
100
+ }
101
+ .transcript-msg-time {
102
+ font-variant-numeric: tabular-nums;
103
+ white-space: nowrap;
104
+ }
105
+ .transcript-msg-sender {
106
+ font-weight: 600;
107
+ color: var(--td-text);
108
+ }
109
+ .transcript-msg-body {
110
+ font-size: 0.9375rem;
111
+ color: #334155;
112
+ white-space: pre-wrap;
113
+ word-break: break-word;
114
+ overflow-wrap: anywhere;
115
+ }
116
+ .transcript-footer {
117
+ margin-top: 1.25rem;
118
+ text-align: center;
119
+ }
120
+ .transcript-powered-by {
121
+ margin: 0;
122
+ font-size: 0.8125rem;
123
+ color: var(--td-muted);
124
+ font-weight: 500;
125
+ }
126
+
127
+ title= (brandName ? brandName + " Chat Transcript" : (title ? title + " Chat Transcript" : "Chat Transcript"))
128
+ //- link(rel='stylesheet', href='/stylesheets/style.css')
129
+ body
130
+ block content
@@ -1,27 +1,28 @@
1
- extends layout
1
+ extends messages-layout
2
2
 
3
3
  block content
4
- div(class="transcript-container")
4
+ div.transcript-page
5
+ header.transcript-header
6
+ if brandLogo
7
+ img.transcript-brand-logo(src=brandLogo alt=(brandName || 'Brand') + ' logo')
8
+ //- else if !brandLogo && !brandName
9
+ //- img.transcript-brand-logo(src="https://tiledesk.com/wp-content/uploads/2022/07/tiledesk_v2.png" alt="Tiledesk logo")
5
10
 
6
- if brandLogo
7
- img.className#IdName(src=brandLogo alt="brand logo")
8
- else if !brandLogo && !brandName
9
- img.className#IdName(src="https://tiledesk.com/wp-content/uploads/2022/07/tiledesk_v2.png" alt="tiledesk logo")
10
-
11
- div(class="transcript-wrapper")
12
- h1(class="transcript-title") Chat Transcript
13
-
14
-
15
- each message in messages
16
- p(class="transcript-p")
17
- span(class="transcript-chat-date") [#{message.createdAt.toLocaleString('en', { timeZone: 'UTC' })}]
18
- span(class="transcript-chat-sender") #{message.senderFullname}:
19
- span(class="transcript-chat-msg") #{message.text}
20
-
21
- if brandName
22
- p(class="transcript-powered-by") Powered by #{brandName}
23
- else
24
- p(class="transcript-powered-by") Powered by
25
- a(href="https://tiledesk.com/", target="_blank") Tiledesk
11
+ header.transcript-title-container
12
+ h1.transcript-title Chat transcript
26
13
 
14
+ main.transcript-wrapper
15
+ ol.transcript-thread(role="list")
16
+ each message in messages
17
+ li.transcript-msg
18
+ div.transcript-msg-sender #{message.senderFullname || 'Unknown'}
19
+ div.transcript-msg-body #{message.text}
20
+ div.transcript-msg-meta
21
+ time.transcript-msg-time(datetime=message.createdAt ? message.createdAt.toISOString() : '') #{message.transcriptDisplayTime}
22
+ //- span.transcript-msg-sender #{message.senderFullname || 'Unknown'}
27
23
 
24
+ footer.transcript-footer
25
+ if brandName
26
+ p.transcript-powered-by Powered by #{brandName}
27
+ else
28
+ p.transcript-powered-by Powered by #[a(href="https://tiledesk.com/", target="_blank", rel="noopener noreferrer") Tiledesk]
@@ -2,7 +2,11 @@ extends layout
2
2
 
3
3
  block content
4
4
  div(class="transcript-container")
5
- img.className#IdName(src="https://tiledesk.com/wp-content/uploads/2022/07/tiledesk_v2.png" alt="tiledesk logo")
5
+
6
+ if brandLogo
7
+ img.className#IdName(src=brandLogo alt="brand logo")
8
+ else if !brandLogo && !brandName
9
+ img.className#IdName(src="https://tiledesk.com/wp-content/uploads/2022/07/tiledesk_v2.png" alt="tiledesk logo")
6
10
 
7
11
  div(class="transcript-wrapper")
8
12
  h1(class="transcript-title") Chat Transcript
@@ -13,8 +17,11 @@ block content
13
17
  span(class="transcript-chat-date") [#{message.createdAt.toLocaleString('en', { timeZone: 'UTC' })}]
14
18
  span(class="transcript-chat-sender") #{message.senderFullname}:
15
19
  span(class="transcript-chat-msg") #{message.text}
16
-
17
- p(class="transcript-powered-by") Powered by
18
- a(href="https://tiledesk.com/", target="_blank") tiledesk
20
+
21
+ if brandName
22
+ p(class="transcript-powered-by") Powered by #{brandName}
23
+ else
24
+ p(class="transcript-powered-by") Powered by
25
+ a(href="https://tiledesk.com/", target="_blank") Tiledesk
19
26
 
20
27
 
package/.env.sample DELETED
@@ -1,141 +0,0 @@
1
- # Global properties:
2
- # See https://developer.tiledesk.com/ for more information regarding these properties.
3
-
4
- #PORT=3000
5
- #Default is 3000
6
-
7
- #LOG_LEVEL=debug
8
-
9
- #GLOBAL_SECRET=YOUR_JWT_SECRET
10
- #Default is nodeauthsecret
11
-
12
- #ADMIN_EMAIL=your_admin_email
13
- #Default is: admin@tiledesk.com
14
-
15
- #ADMIN_PASSWORD=your_password;
16
- #Default is: adminadmin
17
-
18
- #SUPER_PASSWORD=TILEDESK_SUPER_ADMIN_PASSWORD
19
- #Default is: nodeauthsecret
20
-
21
- #CREATE_INITIAL_DATA=false
22
-
23
- ## Database
24
-
25
- #MONGODB_URI=YOUR_MONGO_URI
26
- #Default is : mongodb://localhost/tiledesk
27
-
28
- #MONGODB_LOGS_URI=YOUR_MONGO_URI_FOR_LOGGING
29
- #Default is : mongodb://localhost/tiledesk-logs
30
-
31
- ## Email
32
-
33
- #EMAIL_ENABLED=true
34
- #Default is false
35
-
36
- #EMAIL_BASEURL=https://YOOURDOMAIN.com/dashboard
37
- #Default is : https://console.tiledesk.com/v2/dashboard
38
-
39
- #EMAIL_HOST=YOUR_EMAIL_HOST
40
- #EMAIL_USERNAME=YOUR_EMAIL_USERNAME
41
- #EMAIL_SECURE=true #defaults to 587 if is secure is false or 465 if true
42
- #EMAIL_PORT=25
43
- #EMAIL_PASSWORD=YOUR_SMTP_PASSWORD
44
- #EMAIL_FROM_ADDRESS=FROM_ADDRESS
45
-
46
-
47
- #For multiline support see here:
48
- #dotenv -> https://github.com/bkeepers/dotenv#multi-line-values
49
-
50
- #EMAIL_ASSIGN_REQUEST_HTML_TEMPLATE=
51
- #EMAIL_POOLED_REQUEST_HTML_TEMPLATE=
52
- #EMAIL_RESET_PASSWORD_HTML_TEMPLATE=
53
- #EMAIL_PASSWORD_CHANGED_HTML_TEMPLATE=
54
- #EMAIL_EXUSER_INVITED_HTML_TEMPLATE=
55
- #EMAIL_NEWUSER_INVITED_HTML_TEMPLATE=
56
- #EMAIL_VERIFY_HTML_TEMPLATE=
57
- #EMAIL_SEND_TRANSCRIPT_HTML_TEMPLATE=
58
-
59
-
60
- #DISABLE_TRANSCRIPT_VIEW_PAGE=true
61
- #Disable view transcript messages using request_id field. Pay attention to security for this feature. Default is false
62
-
63
- # Chat21 channel. ATTENTION!!!! THESE PROPERTIES MUST BE CONFIGURED.
64
-
65
- # important
66
- #FIREBASE_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\***************\n-----END PRIVATE KEY-----\n
67
- #FIREBASE_CLIENT_EMAIL=firebase-adminsdk-******@************.iam.gserviceaccount.com
68
- #FIREBASE_PROJECT_ID=CHANGEIT
69
-
70
- # Chat Engine
71
-
72
- CHAT21_ENABLED=true
73
-
74
- CHAT21_ENGINE=mqtt
75
- #CHAT21_ENGINE=firebase
76
-
77
- # Native Chat Engine
78
- CHAT21_URL=http://localhost:8004
79
-
80
- # Firebase Chat Engine
81
- #CHAT21_URL=https://CHANGEIT.cloudfunctions.net
82
-
83
- # optional properties
84
- #CHAT21_APPID=tilechat
85
- #Default is tilechat
86
-
87
- CHAT21_JWT_SECRET=tokenKey
88
- #Used by Native ChatEngine for generating JWT tokens
89
-
90
- # Native Chat Engine
91
- CHAT21_ADMIN_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzNDU2MTBkNy03MmYxLTQ5OWUtODQzMS0xMTNhZWJkOWVmOTgiLCJzdWIiOiIxMDAtQVBJQURNSU4iLCJzY29wZSI6WyJyYWJiaXRtcS5yZWFkOiovKi8qIiwicmFiYml0bXEud3JpdGU6Ki8qLyoiLCJyYWJiaXRtcS5jb25maWd1cmU6Ki8qLyoiXSwiY2xpZW50X2lkIjoiMTAwLUFQSUFETUlOIiwiY2lkIjoiMTAwLUFQSUFETUlOIiwiYXpwIjoiMTAwLUFQSUFETUlOIiwidXNlcl9pZCI6IjEwMC1BUElBRE1JTiIsImFwcF9pZCI6InRpbGVjaGF0IiwiaWF0IjoxNjE0OTQzNDkyLCJleHAiOjE5MjU5ODM0OTIsImF1ZCI6WyJyYWJiaXRtcSIsIjEwMC1BUElBRE1JTiJdLCJraWQiOiJ0aWxlZGVzay1rZXkiLCJ0aWxlZGVza19hcGlfcm9sZXMiOiJhZG1pbiJ9.KptGMsTKjd3wUiiP1GGSTYqW1XXK1vjSRJnetC3wjxU
92
-
93
- # Firebase Chat Engine
94
- #CHAT21_ADMIN_TOKEN=chat21-secret-orgAa,
95
- #Default for firebase engine is: chat21-secret-orgAa,
96
-
97
-
98
-
99
-
100
- # optional properties for tiledesk-server docker installation but required for docker-compose used by clients (dashboard, ionic, widget)
101
- # important
102
- #FIREBASE_APIKEY=CHANGEIT
103
- #FIREBASE_AUTHDOMAIN=CHANGEIT.firebaseapp.com
104
- #FIREBASE_DATABASEURL=https://CHANGEIT.firebaseio.com
105
- #FIREBASE_STORAGEBUCKET=CHANGEIT.appspot.com
106
- #FIREBASE_MESSAGINGSENDERID=********
107
- #FIREBASE_APP_ID=CHANGEIT
108
-
109
-
110
-
111
-
112
-
113
- #WS_URL=ws://localhost:3000/
114
- # WebSocket endpoint.
115
-
116
- ## Widget settings
117
-
118
- # important
119
- WIDGET_LOCATION=http://localhost:4200/launch.js
120
- #Default is http://localhost:4200/
121
-
122
- WIDGET_TEST_LOCATION=http://localhost:4200/assets/test_widget_page/index.html
123
-
124
-
125
- #WS_HISTORY_REQUESTS_LIMIT=50
126
- #Default is 100
127
-
128
-
129
- #WS_SERVER_PATH=/v2/ws
130
- #Default is /
131
-
132
- #WEBHOOK_ORIGIN=https://api.YOURDOMAIN.com
133
- #Default is http://localhost:3000
134
-
135
- # Enterprise modules. For enterprise modules configuration please refer to https://docs.tiledesk.com/knowledge-base/install-tiledesk-enterprise-with-kubernetes/
136
-
137
- #Enable and configure these properties if you are using npm (or Heroku) installation. This is not required if you are using Docker Enterprise images.
138
- #ENABLE_ENTERPRISE_MODULE=true
139
-
140
- #NPM_TOKEN=XXXX
141
- #Enable enterprise module with npm private token