-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstart.py
executable file
·197 lines (147 loc) · 6.11 KB
/
start.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#!/usr/local/bin/python3
import os
import sqlite3
import subprocess
import sys
import time
from sqlite3 import Cursor
from urllib.request import Request, urlopen
import jinja2
database_path = '/automx2_db.sqlite'
template_path = '/etc/automx2.template.conf'
configuration_path = '/etc/automx2.conf'
custom_sql_path = '/data/custom.sql'
def check_required_env():
"""
Checks if the required environment variables are set
"""
# If a SQL file is provided, not all environment variables are required
if os.path.isfile(custom_sql_path):
required_env_variables = ['PROXY_COUNT', 'PROVIDER_NAME', 'PROVIDER_SHORTNAME', 'DOMAINS']
else:
required_env_variables = ['PROXY_COUNT', 'PROVIDER_NAME', 'PROVIDER_SHORTNAME', 'DOMAINS']
missing_env_variables = [v for v in required_env_variables if os.getenv(v) is None]
if len(missing_env_variables) > 0:
print(f'Error: The environment variables {missing_env_variables} must be set')
sys.exit(1)
def create_configuration():
"""
Creates the configuration file for automx2 by applying the environment variables to the template.
check_required_env() must be called beforehand.
"""
with open(template_path, 'r') as template_file:
template = template_file.read()
result = jinja2.Template(template).render({'proxy_count': os.getenv('PROXY_COUNT')})
with open(configuration_path, 'w') as configuration_file:
configuration_file.write(result)
def create_database():
"""
Creates a SQLite database at the given path and populates with the default tables and entries.
create_configuration() must be called beforehand.
"""
# Deletes the database file if it already exists
if os.path.isfile(database_path):
os.remove(database_path)
# Runs automx2 with gunicorn as the application server as a subprocess to populate the database
# https://flask.palletsprojects.com/en/2.0.x/deploying/wsgi-standalone/#gunicorn
# https://docs.python.org/3.10/library/subprocess.html#subprocess.Popen
process = subprocess.Popen(['/usr/local/bin/gunicorn', '-b 127.0.0.1:80', 'automx2.server:app'])
# Send a request to the /initdb/ endpoint to generate the database schema
# https://rseichter.github.io/automx2/#_testing_standalone_automx2
initdb_request = Request('http://127.0.0.1/initdb/')
time.sleep(1)
with urlopen(initdb_request) as response:
if response.status != 200:
print('Unable to create the database schema')
sys.exit(1)
# Once the database schema is created, we'll stop automx2 to edit the database
process.terminate()
def populate_database():
"""
Decides which method to use for database population.
create_database() must be called beforehand.
See: https://docs.python.org/3/library/sqlite3.html
"""
connection = sqlite3.connect(database_path)
cursor = connection.cursor()
remove_database_defaults(cursor)
if os.path.isfile(custom_sql_path):
populate_database_file(cursor)
else:
populate_database_env(cursor)
cursor.close()
connection.commit()
connection.close()
def remove_database_defaults(cursor: Cursor):
"""
Removes the default contents of the database.
See: https://github.com/rseichter/automx2/blob/master/automx2/model.py
"""
cursor.execute('DELETE FROM provider')
cursor.execute('DELETE FROM ldapserver')
cursor.execute('DELETE FROM davserver')
cursor.execute('DELETE FROM server')
cursor.execute('DELETE FROM domain')
cursor.execute('DELETE FROM server_domain')
def populate_database_file(cursor: Cursor):
"""
Inserts data based on a user-provided SQL script.
"""
with open(custom_sql_path, 'r') as custom_sql_file:
custom_sql = custom_sql_file.read()
cursor.executescript(custom_sql)
def populate_database_env(cursor: Cursor):
"""
Inserts data based on the environment variables.
See: https://github.com/rseichter/automx2/blob/master/contrib/sqlite-generate.sh
"""
# Table: provider
provider_id = 0
provider_name = os.getenv('PROVIDER_NAME', 'Default name')
provider_shortname = os.getenv('PROVIDER_SHORTNAME', 'Default')
cursor.execute(
'INSERT INTO provider(id, name, short_name) VALUES(?, ?, ?)',
(0, provider_name, provider_shortname)
)
# Table: server
def server_from_env(identifier: int, server_type: str) -> list[tuple]:
host = os.getenv(f'{server_type.upper()}_HOST')
port = os.getenv(f'{server_type.upper()}_PORT')
socket = os.getenv(f'{server_type.upper()}_SOCKET')
if not host:
return []
return [(identifier, 10, port, server_type, host, socket, '%EMAILADDRESS%', 'plain')]
servers = []
servers += server_from_env(0, 'imap')
servers += server_from_env(1, 'pop')
servers += server_from_env(2, 'smtp')
cursor.executemany(
'INSERT INTO server(id, prio, port, type, name, socket_type, user_name, authentication) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
servers
)
if len(servers) == 0:
print('Warning: No server configured')
# Table: domain
domains = []
domain_strings = os.getenv('DOMAINS').split(',')
for index, domain_string in enumerate(domain_strings):
domains += [(index, domain_string, provider_id, None)]
cursor.executemany('INSERT INTO domain(id, name, provider_id, ldapserver_id) VALUES (?, ?, ?, ?)', domains)
# Table: server_domain
server_domains = []
for domain in domains:
for server in servers:
server_domains += [(server[0], domain[0])]
cursor.executemany('INSERT INTO server_domain(server_id, domain_id) VALUES(?, ?)', server_domains)
def replace_with_app():
"""
Replaces this process with actual automx2 application running with the gunicorn application server.
populate_database() must be called beforehand.
"""
# Replace this process with the flask application server
os.execv('/usr/local/bin/gunicorn', ['gunicorn', '-b 0.0.0.0:80', 'automx2.server:app'])
check_required_env()
create_configuration()
create_database()
populate_database()
replace_with_app()