Skip to content

Commit 7211e46

Browse files
authored
Merge pull request #128 from PFConnect/canary
Canary
2 parents 3307c13 + db3bf23 commit 7211e46

8 files changed

Lines changed: 112 additions & 59 deletions

File tree

.env.example

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
VITE_SERVER_URL=http://localhost:9901 # Make sure this matches the PORT value below (in development)
22
FRONTEND_URL=http://localhost:5173 # Make sure this matches the vite.config.ts
33

4-
POSTGRES_DB_URL=postgresql://pfcontrol:YOUR_POSTGRES_PASSWORD_HERE@localhost:5432/pfcontrol
5-
POSTGRES_DB_URL_FLIGHTS=postgresql://pfcontrol:YOUR_POSTGRES_PASSWORD_HERE@localhost:5432/pfcontrol_flights
6-
POSTGRES_DB_URL_CHATS=postgresql://pfcontrol:YOUR_POSTGRES_PASSWORD_HERE@localhost:5432/pfcontrol_chats
7-
POSTGRES_DB_PW=YOUR_POSTGRES_PASSWORD_HERE
4+
POSTGRES_DB_URL=postgresql://pfcontrol:pfcontrol@localhost:5432/pfcontrol
5+
POSTGRES_DB_URL_FLIGHTS=postgresql://pfcontrol:pfcontrol@localhost:5432/pfcontrol_flights
6+
POSTGRES_DB_URL_CHATS=postgresql://pfcontrol:pfcontrol@localhost:5432/pfcontrol_chats
7+
POSTGRES_DB_PW=pfcontrol
88
DB_ENCRYPTION_KEY=YOUR_ENCRYPTION_KEY_HERE # Must be 128 Characters Long - use this in terminal: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
99
REDIS_URL=redis://localhost:6379
1010

server/db/schemas.ts

Lines changed: 98 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,45 +5,79 @@ export async function createMainTables() {
55
await mainDb.schema
66
.createTable('app_settings')
77
.ifNotExists()
8-
.addColumn('id', 'integer', (col) => col.primaryKey())
8+
.addColumn('id', 'serial', (col) => col.primaryKey())
99
.addColumn('version', 'varchar(50)', (col) => col.notNull())
1010
.addColumn('updated_at', 'timestamp', (col) => col.notNull())
1111
.addColumn('updated_by', 'varchar(255)', (col) => col.notNull())
1212
.execute();
1313

14-
// users (assuming based on typical UsersTable interface)
14+
// roles (must be created before users because users references roles)
1515
await mainDb.schema
16-
.createTable('users')
16+
.createTable('roles')
1717
.ifNotExists()
18-
.addColumn('id', 'varchar(255)', (col) => col.primaryKey())
19-
.addColumn('username', 'varchar(255)', (col) => col.unique().notNull())
20-
.addColumn('email', 'varchar(255)', (col) => col.unique().notNull())
21-
.addColumn('password_hash', 'varchar(255)', (col) => col.notNull())
18+
.addColumn('id', 'serial', (col) => col.primaryKey())
19+
.addColumn('name', 'varchar(255)', (col) => col.unique().notNull())
20+
.addColumn('description', 'text')
21+
.addColumn('permissions', 'jsonb', (col) => col.notNull())
22+
.addColumn('color', 'varchar(50)')
23+
.addColumn('icon', 'varchar(255)')
24+
.addColumn('priority', 'integer')
2225
.addColumn('created_at', 'timestamp', (col) => col.defaultTo('now()'))
2326
.addColumn('updated_at', 'timestamp', (col) => col.defaultTo('now()'))
24-
.addColumn('statistics', 'jsonb', (col) => col.defaultTo('{}'))
2527
.execute();
2628

27-
// sessions
29+
// users
2830
await mainDb.schema
29-
.createTable('sessions')
31+
.createTable('users')
3032
.ifNotExists()
3133
.addColumn('id', 'varchar(255)', (col) => col.primaryKey())
32-
.addColumn('session_id', 'varchar(255)', (col) => col.notNull())
33-
.addColumn('user_id', 'varchar(255)', (col) =>
34-
col.references('users.id').onDelete('cascade')
35-
)
34+
.addColumn('username', 'varchar(255)', (col) => col.notNull())
35+
.addColumn('discriminator', 'varchar(10)', (col) => col.notNull())
36+
.addColumn('avatar', 'text')
37+
.addColumn('access_token', 'text')
38+
.addColumn('refresh_token', 'text')
39+
.addColumn('last_login', 'timestamp')
40+
.addColumn('ip_address', 'text')
41+
.addColumn('is_vpn', 'boolean')
42+
.addColumn('sessions', 'text')
43+
.addColumn('last_session_created', 'timestamp')
44+
.addColumn('last_session_deleted', 'timestamp')
45+
.addColumn('settings', 'text')
46+
.addColumn('settings_updated_at', 'timestamp')
47+
.addColumn('total_sessions_created', 'integer', (col) => col.defaultTo(0))
48+
.addColumn('total_minutes', 'integer', (col) => col.defaultTo(0))
49+
.addColumn('vatsim_cid', 'varchar(50)')
50+
.addColumn('vatsim_rating_id', 'integer')
51+
.addColumn('vatsim_rating_short', 'varchar(10)')
52+
.addColumn('vatsim_rating_long', 'varchar(255)')
3653
.addColumn('created_at', 'timestamp', (col) => col.defaultTo('now()'))
37-
.addColumn('expires_at', 'timestamp')
54+
.addColumn('updated_at', 'timestamp', (col) => col.defaultTo('now()'))
55+
.addColumn('roblox_user_id', 'varchar(255)')
56+
.addColumn('roblox_username', 'varchar(255)')
57+
.addColumn('roblox_access_token', 'text')
58+
.addColumn('roblox_refresh_token', 'text')
59+
.addColumn('role_id', 'integer', (col) =>
60+
col.references('roles.id').onDelete('set null')
61+
)
62+
.addColumn('tutorialCompleted', 'boolean', (col) => col.defaultTo(false))
63+
.addColumn('statistics', 'text')
3864
.execute();
3965

40-
// roles
66+
// sessions
4167
await mainDb.schema
42-
.createTable('roles')
68+
.createTable('sessions')
4369
.ifNotExists()
44-
.addColumn('id', 'serial', (col) => col.primaryKey())
45-
.addColumn('name', 'varchar(255)', (col) => col.unique().notNull())
46-
.addColumn('description', 'text')
70+
.addColumn('session_id', 'varchar(255)', (col) => col.primaryKey())
71+
.addColumn('access_id', 'varchar(255)', (col) => col.notNull())
72+
.addColumn('active_runway', 'varchar(10)')
73+
.addColumn('airport_icao', 'varchar(10)', (col) => col.notNull())
74+
.addColumn('created_at', 'timestamp', (col) => col.defaultTo('now()'))
75+
.addColumn('created_by', 'varchar(255)', (col) => col.notNull())
76+
.addColumn('is_pfatc', 'boolean', (col) => col.defaultTo(false))
77+
.addColumn('flight_strips', 'text')
78+
.addColumn('atis', 'text')
79+
.addColumn('custom_name', 'varchar(255)')
80+
.addColumn('refreshed_at', 'timestamp')
4781
.execute();
4882

4983
// user_roles
@@ -56,6 +90,7 @@ export async function createMainTables() {
5690
.addColumn('role_id', 'integer', (col) =>
5791
col.references('roles.id').onDelete('cascade')
5892
)
93+
.addColumn('assigned_at', 'timestamp', (col) => col.defaultTo('now()'))
5994
.addPrimaryKeyConstraint('user_roles_pkey', ['user_id', 'role_id'])
6095
.execute();
6196

@@ -64,36 +99,44 @@ export async function createMainTables() {
6499
.createTable('audit_log')
65100
.ifNotExists()
66101
.addColumn('id', 'serial', (col) => col.primaryKey())
67-
.addColumn('user_id', 'varchar(255)', (col) =>
68-
col.references('users.id').onDelete('set null')
69-
)
70-
.addColumn('action', 'varchar(255)', (col) => col.notNull())
102+
.addColumn('admin_id', 'varchar(255)', (col) => col.notNull())
103+
.addColumn('admin_username', 'varchar(255)', (col) => col.notNull())
104+
.addColumn('action_type', 'varchar(100)', (col) => col.notNull())
105+
.addColumn('target_user_id', 'varchar(255)')
106+
.addColumn('target_username', 'varchar(255)')
71107
.addColumn('details', 'jsonb')
108+
.addColumn('ip_address', 'text')
109+
.addColumn('user_agent', 'text')
72110
.addColumn('timestamp', 'timestamp', (col) => col.defaultTo('now()'))
111+
.addColumn('created_at', 'timestamp', (col) => col.defaultTo('now()'))
73112
.execute();
74113

75114
// bans
76115
await mainDb.schema
77116
.createTable('bans')
78117
.ifNotExists()
79118
.addColumn('id', 'serial', (col) => col.primaryKey())
80-
.addColumn('user_id', 'varchar(255)', (col) =>
81-
col.references('users.id').onDelete('cascade').notNull()
82-
)
119+
.addColumn('user_id', 'varchar(255)')
120+
.addColumn('ip_address', 'text')
121+
.addColumn('username', 'varchar(255)')
83122
.addColumn('reason', 'text')
123+
.addColumn('banned_by', 'varchar(255)', (col) => col.notNull())
84124
.addColumn('banned_at', 'timestamp', (col) => col.defaultTo('now()'))
85125
.addColumn('expires_at', 'timestamp')
126+
.addColumn('active', 'boolean', (col) => col.defaultTo(true))
86127
.execute();
87128

88129
// notifications
89130
await mainDb.schema
90131
.createTable('notifications')
91132
.ifNotExists()
92133
.addColumn('id', 'serial', (col) => col.primaryKey())
93-
.addColumn('title', 'varchar(255)', (col) => col.notNull())
94-
.addColumn('message', 'text', (col) => col.notNull())
134+
.addColumn('type', 'varchar(50)', (col) => col.notNull())
135+
.addColumn('text', 'text', (col) => col.notNull())
95136
.addColumn('show', 'boolean', (col) => col.defaultTo(true))
137+
.addColumn('custom_color', 'varchar(50)')
96138
.addColumn('created_at', 'timestamp', (col) => col.defaultTo('now()'))
139+
.addColumn('updated_at', 'timestamp', (col) => col.defaultTo('now()'))
97140
.execute();
98141

99142
// user_notifications
@@ -102,13 +145,13 @@ export async function createMainTables() {
102145
.ifNotExists()
103146
.addColumn('id', 'serial', (col) => col.primaryKey())
104147
.addColumn('user_id', 'varchar(255)', (col) =>
105-
col.references('users.id').onDelete('cascade')
106-
)
107-
.addColumn('notification_id', 'integer', (col) =>
108-
col.references('notifications.id').onDelete('cascade')
148+
col.references('users.id').onDelete('cascade').notNull()
109149
)
150+
.addColumn('type', 'varchar(50)', (col) => col.notNull())
151+
.addColumn('title', 'varchar(255)', (col) => col.notNull())
152+
.addColumn('message', 'text', (col) => col.notNull())
110153
.addColumn('read', 'boolean', (col) => col.defaultTo(false))
111-
.addColumn('read_at', 'timestamp')
154+
.addColumn('created_at', 'timestamp', (col) => col.defaultTo('now()'))
112155
.execute();
113156

114157
// testers
@@ -119,7 +162,12 @@ export async function createMainTables() {
119162
.addColumn('user_id', 'varchar(255)', (col) =>
120163
col.references('users.id').onDelete('cascade').unique().notNull()
121164
)
122-
.addColumn('approved', 'boolean', (col) => col.defaultTo(false))
165+
.addColumn('username', 'varchar(255)', (col) => col.notNull())
166+
.addColumn('added_by', 'varchar(255)', (col) => col.notNull())
167+
.addColumn('added_by_username', 'varchar(255)', (col) => col.notNull())
168+
.addColumn('notes', 'text')
169+
.addColumn('created_at', 'timestamp', (col) => col.defaultTo('now()'))
170+
.addColumn('updated_at', 'timestamp', (col) => col.defaultTo('now()'))
123171
.execute();
124172

125173
// tester_settings
@@ -129,15 +177,21 @@ export async function createMainTables() {
129177
.addColumn('id', 'serial', (col) => col.primaryKey())
130178
.addColumn('setting_key', 'varchar(255)', (col) => col.unique().notNull())
131179
.addColumn('setting_value', 'boolean', (col) => col.notNull())
180+
.addColumn('updated_at', 'timestamp', (col) => col.defaultTo('now()'))
132181
.execute();
133182

134183
// daily_statistics
135184
await mainDb.schema
136185
.createTable('daily_statistics')
137186
.ifNotExists()
138-
.addColumn('date', 'date', (col) => col.primaryKey())
139-
.addColumn('flights_count', 'integer', (col) => col.defaultTo(0))
140-
.addColumn('users_count', 'integer', (col) => col.defaultTo(0))
187+
.addColumn('id', 'serial', (col) => col.primaryKey())
188+
.addColumn('date', 'date', (col) => col.notNull().unique())
189+
.addColumn('logins_count', 'integer', (col) => col.defaultTo(0))
190+
.addColumn('new_sessions_count', 'integer', (col) => col.defaultTo(0))
191+
.addColumn('new_flights_count', 'integer', (col) => col.defaultTo(0))
192+
.addColumn('new_users_count', 'integer', (col) => col.defaultTo(0))
193+
.addColumn('created_at', 'timestamp', (col) => col.defaultTo('now()'))
194+
.addColumn('updated_at', 'timestamp', (col) => col.defaultTo('now()'))
141195
.execute();
142196

143197
// chat_report
@@ -186,7 +240,7 @@ export async function createMainTables() {
186240
.addColumn('old_data', 'jsonb')
187241
.addColumn('new_data', 'jsonb')
188242
.addColumn('ip_address', 'varchar(255)')
189-
.addColumn('timestamp', 'timestamp', (col) => col.defaultTo('now()'))
243+
.addColumn('timestamp', 'timestamp', (col) => col.notNull().defaultTo('now()'))
190244
.execute();
191245

192246
// feedback
@@ -213,14 +267,15 @@ export async function createMainTables() {
213267
.addColumn('path', 'text', (col) => col.notNull())
214268
.addColumn('status_code', 'integer', (col) => col.notNull())
215269
.addColumn('response_time', 'integer', (col) => col.notNull())
216-
.addColumn('ip_address', 'text')
270+
.addColumn('ip_address', 'text', (col) => col.notNull())
217271
.addColumn('user_agent', 'text')
218272
.addColumn('request_body', 'text')
219273
.addColumn('response_body', 'text')
220274
.addColumn('error_message', 'text')
221-
.addColumn('timestamp', 'timestamp', (col) => col.defaultTo('now()'))
275+
.addColumn('timestamp', 'timestamp', (col) => col.notNull().defaultTo('now()'))
222276
.execute();
223277

278+
// api_logs indexes
224279
try {
225280
await mainDb.schema
226281
.createIndex('idx_api_logs_timestamp')
@@ -255,6 +310,7 @@ export async function createMainTables() {
255310
.column('status_code')
256311
.execute();
257312

313+
// controller_ratings
258314
await mainDb.schema
259315
.createTable('controller_ratings')
260316
.ifNotExists()
@@ -276,7 +332,7 @@ export async function createFlightsTable(sessionId: string) {
276332
.addColumn('id', 'varchar(255)', (col) => col.primaryKey())
277333
.addColumn('session_id', 'varchar(255)', (col) => col.notNull())
278334
.addColumn('user_id', 'varchar(255)')
279-
.addColumn('ip_address', 'varchar(45)')
335+
.addColumn('ip_address', 'text')
280336
.addColumn('callsign', 'varchar(255)')
281337
.addColumn('aircraft', 'varchar(255)')
282338
.addColumn('flight_type', 'varchar(50)')

server/main.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ app.use(
6666
origin:
6767
process.env.NODE_ENV === 'production'
6868
? [
69-
'https://control.pfconnect.online',
70-
'https://canary.pfconnect.online',
69+
'https://pfcontrol.com',
70+
'https://canary.pfcontrol.com',
7171
]
7272
: ['http://localhost:9901', 'http://localhost:5173'],
7373
credentials: true,

src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export default function App() {
9292

9393
const settings = await getTesterSettings();
9494

95-
if (settings && typeof settings.tester_gate_enabled === 'boolean') {
95+
if (settings) {
9696
setTesterGateEnabled(settings.tester_gate_enabled);
9797
} else {
9898
console.error(
@@ -107,7 +107,7 @@ export default function App() {
107107
}
108108
};
109109

110-
checkGateStatus();
110+
checkGateStatus().then();
111111
}, []);
112112

113113
const handleCloseModal = () => {

src/components/ProtectedRoute.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export default function ProtectedRoute({
4444

4545
const settings = await getTesterSettings();
4646

47-
if (settings && typeof settings.tester_gate_enabled === 'boolean') {
47+
if (settings) {
4848
setTesterGateEnabled(settings.tester_gate_enabled);
4949
} else {
5050
console.error(
@@ -59,7 +59,7 @@ export default function ProtectedRoute({
5959
}
6060
};
6161

62-
checkGateStatus();
62+
checkGateStatus().then();
6363
}, [requireTester, user]);
6464

6565
if (isLoading || (requireTester && testerGateEnabled === null)) {

src/components/tools/ATIS.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ export default function ATIS({
9292
let atisData = null;
9393
if (
9494
typeof sessionData.atis === 'object' &&
95-
sessionData.atis !== null &&
9695
icao in sessionData.atis
9796
) {
9897
// @ts-expect-error: dynamic key access
@@ -206,12 +205,12 @@ export default function ATIS({
206205
}
207206
};
208207

209-
loadPreviousATIS();
208+
loadPreviousATIS().then();
210209
}, [sessionId, accessId, icao, open, availableRunways]);
211210

212211
useEffect(() => {
213212
if (icao && !fetchedAirports.has(icao)) {
214-
fetchAirportData(icao);
213+
fetchAirportData(icao).then();
215214
}
216215
}, [icao, fetchedAirports, fetchAirportData]);
217216

src/components/tools/ChartDrawer.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,14 +141,12 @@ export default function ChartDrawer({
141141
(icao) => icao !== departureAirport && icao !== arrivalAirport
142142
);
143143

144-
const allChartsForLegacy = [
144+
const chartsToUse = [
145145
...departureCharts,
146146
...arrivalCharts,
147147
...otherAirports.flatMap((icao) => getChartsForAirport(icao)),
148148
];
149149

150-
const chartsToUse = allChartsForLegacy;
151-
152150
const filteredDepartureCharts = departureCharts.filter(
153151
(chart) =>
154152
chart.name.toLowerCase().includes(searchQuery.toLowerCase()) ||

src/utils/hateSpeechFilter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ function normalizeForProfanity(s: string) {
2020
}
2121

2222
export function containsProfanity(message: string): boolean {
23-
if (!message || typeof message !== 'string') return false;
23+
if (!message) return false;
2424
const raw = message.toLowerCase().trim();
2525

2626
try {

0 commit comments

Comments
 (0)