Skip to content

Commit 777827f

Browse files
committed
Italian Promises and Letter Trails.
1 parent 3d65655 commit 777827f

212 files changed

Lines changed: 17475 additions & 5 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
5+
version="6.0">
6+
7+
<display-name>AE6E66</display-name>
8+
9+
<welcome-file-list>
10+
<welcome-file>index.jsp</welcome-file>
11+
</welcome-file-list>
12+
13+
<!-- Security: prevent direct access to WEB-INF and configuration -->
14+
<security-constraint>
15+
<web-resource-collection>
16+
<web-resource-name>Protected</web-resource-name>
17+
<url-pattern>/WEB-INF/*</url-pattern>
18+
<url-pattern>/META-INF/*</url-pattern>
19+
</web-resource-collection>
20+
<auth-constraint/>
21+
</security-constraint>
22+
23+
<!-- Security headers via filter -->
24+
<filter>
25+
<filter-name>SecurityHeaders</filter-name>
26+
<filter-class>com.mearvk.ae6e66.SecurityHeadersFilter</filter-class>
27+
</filter>
28+
<filter-mapping>
29+
<filter-name>SecurityHeaders</filter-name>
30+
<url-pattern>/*</url-pattern>
31+
</filter-mapping>
32+
33+
<!-- Session timeout: 30 minutes -->
34+
<session-config>
35+
<session-timeout>30</session-timeout>
36+
<cookie-config>
37+
<http-only>true</http-only>
38+
<secure>false</secure>
39+
</cookie-config>
40+
</session-config>
41+
42+
<!-- Error pages -->
43+
<error-page>
44+
<error-code>404</error-code>
45+
<location>/index.jsp</location>
46+
</error-page>
47+
<error-page>
48+
<error-code>500</error-code>
49+
<location>/index.jsp</location>
50+
</error-page>
51+
52+
<mime-mapping>
53+
<extension>xhtml</extension>
54+
<mime-type>text/html</mime-type>
55+
</mime-mapping>
56+
</web-app>

.preview-serve/ae6e66/contacts.jsp

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
2+
<%@ page import="java.sql.*, java.util.Properties, java.io.*" %>
3+
<!DOCTYPE html>
4+
<html lang="en">
5+
<head>
6+
<meta charset="UTF-8"/>
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
8+
<title>Contacts — AE6E66™</title>
9+
<link rel="stylesheet" href="css/style.css"/>
10+
<script src="js/scroll-preserve.js"></script>
11+
</head>
12+
<body>
13+
<nav class="nav"><div class="nav-inner">
14+
<span class="nav-brand">AE6E66™</span>
15+
<ul class="nav-links">
16+
<li><a href="index.jsp">Overview</a></li>
17+
<li><a href="contacts.jsp" class="active">Contacts</a></li>
18+
<li><a href="sent.jsp">Sent</a></li>
19+
<li><a href="status.jsp">Status</a></li>
20+
</ul>
21+
<div class="nav-actions"><a href="crawl.jsp" class="nav-cta">Crawl</a></div>
22+
</div></nav>
23+
24+
<section class="hero" style="padding:4rem 2rem;">
25+
<div class="hero-inner">
26+
<span class="hero-tag">UK Parliament</span>
27+
<h1>Contacts</h1>
28+
<p>House of Lords + House of Commons members crawled from members.parliament.uk.</p>
29+
</div>
30+
</section>
31+
32+
<section class="section">
33+
<div class="section-inner">
34+
<%
35+
String sourceFilter = request.getParameter("source");
36+
Properties dbProps = new Properties();
37+
boolean propsLoaded = false;
38+
Connection conn = null;
39+
try {
40+
InputStream dbIn = application.getResourceAsStream("/WEB-INF/db.properties");
41+
if (dbIn != null) { dbProps.load(dbIn); dbIn.close(); propsLoaded = true; }
42+
if (!propsLoaded) {
43+
String[] tryPaths = { "/opt/tomcat/webapps/ae6e66/WEB-INF/db.properties" };
44+
for (String tp : tryPaths) { File f = new File(tp);
45+
if (f.exists()) { FileInputStream fis = new FileInputStream(f); dbProps.load(fis); fis.close(); propsLoaded = true; break; } }
46+
}
47+
Class.forName(dbProps.getProperty("db.driver", "com.mysql.cj.jdbc.Driver"));
48+
conn = DriverManager.getConnection(
49+
dbProps.getProperty("db.url", "jdbc:mysql://127.0.0.1:3306/nwe_ae6e66"),
50+
dbProps.getProperty("db.user", "root"),
51+
dbProps.getProperty("db.password", ""));
52+
53+
if (sourceFilter == null || sourceFilter.isEmpty()) {
54+
// Show sources (HOL / HOC)
55+
ResultSet rs = conn.createStatement().executeQuery(
56+
"SELECT source, COUNT(*) AS cnt FROM contacts GROUP BY source ORDER BY source");
57+
%>
58+
<h3>Browse by Chamber</h3>
59+
<div class="table-wrap">
60+
<table>
61+
<thead><tr><th>Chamber</th><th>Members</th></tr></thead>
62+
<tbody>
63+
<% while (rs.next()) { %>
64+
<tr><td><a href="contacts.jsp?source=<%= rs.getString("source") %>"><%= rs.getString("source") %></a></td><td><%= rs.getInt("cnt") %></td></tr>
65+
<% } rs.close(); %>
66+
</tbody>
67+
</table>
68+
</div>
69+
<%
70+
} else {
71+
PreparedStatement ps = conn.prepareStatement(
72+
"SELECT id, name, email, phone, ministry, source FROM contacts WHERE source=? ORDER BY name");
73+
ps.setString(1, sourceFilter);
74+
ResultSet rs = ps.executeQuery();
75+
%>
76+
<h3><%= sourceFilter %> Members</h3>
77+
<p style="margin-bottom:1rem;"><a href="contacts.jsp">← All Chambers</a></p>
78+
<div class="table-wrap">
79+
<table>
80+
<thead><tr><th>ID</th><th>Name</th><th>Email</th><th>Phone</th><th>Ministry</th></tr></thead>
81+
<tbody>
82+
<% while (rs.next()) {
83+
String email = rs.getString("email");
84+
String phone = rs.getString("phone");
85+
%>
86+
<tr>
87+
<td><%= rs.getInt("id") %></td>
88+
<td><%= rs.getString("name") != null ? rs.getString("name") : "" %></td>
89+
<td><%= email != null && !email.isEmpty() ? email : "" %></td>
90+
<td><%= phone != null && !phone.isEmpty() ? phone : "" %></td>
91+
<td><%= rs.getString("ministry") != null ? rs.getString("ministry") : "" %></td>
92+
</tr>
93+
<% } rs.close(); ps.close(); %>
94+
</tbody>
95+
</table>
96+
</div>
97+
<%
98+
}
99+
} catch (Exception e) {
100+
%>
101+
<p style="color:#ef4444;">Database error: <%= e.getMessage() != null ? e.getMessage().replace("<","&lt;") : "unknown" %></p>
102+
<p style="color:#5f7a5f;font-size:0.8rem;">Props loaded: <%= propsLoaded %></p>
103+
<%
104+
} finally {
105+
if (conn != null) try { conn.close(); } catch (Exception ignored) {}
106+
}
107+
%>
108+
</div>
109+
</section>
110+
111+
<footer class="footer"><div><span>&#169; 2026 MEARVK LLC. All rights reserved.</span></div></footer>
112+
</body>
113+
</html>

.preview-serve/ae6e66/crawl.jsp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
2+
<%@ page import="java.io.*" %>
3+
<!DOCTYPE html>
4+
<html lang="en">
5+
<head>
6+
<meta charset="UTF-8"/>
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
8+
<title>Crawl — AE6E66™</title>
9+
<link rel="stylesheet" href="css/style.css"/>
10+
<script src="js/scroll-preserve.js"></script>
11+
</head>
12+
<body>
13+
<nav class="nav"><div class="nav-inner">
14+
<span class="nav-brand">AE6E66™</span>
15+
<ul class="nav-links">
16+
<li><a href="index.jsp">Overview</a></li>
17+
<li><a href="contacts.jsp">Contacts</a></li>
18+
<li><a href="sent.jsp">Sent</a></li>
19+
<li><a href="status.jsp">Status</a></li>
20+
</ul>
21+
<div class="nav-actions"><span class="nav-cta" style="opacity:0.7;cursor:default;">Crawl</span></div>
22+
</div></nav>
23+
24+
<section class="hero" style="padding:4rem 2rem;">
25+
<div class="hero-inner">
26+
<span class="hero-tag">Parliament Crawler</span>
27+
<h1>Crawl Status</h1>
28+
<p>Crawls members.parliament.uk member IDs 0–999 for contact data and portraits.</p>
29+
</div>
30+
</section>
31+
32+
<section class="section">
33+
<div class="section-inner">
34+
<%
35+
// Check last crawl date
36+
String configDir = application.getRealPath("/") != null ?
37+
new File(application.getRealPath("/")).getParent() + "/../../../configuration" :
38+
"/home/mearvk/IdeaProjects/Java.Web.Server.Telnet.Front.Java.21/modules/AE6E66/configuration";
39+
File lastCrawlFile = new File(configDir, ".last-crawl");
40+
String lastCrawl = "Never";
41+
boolean crawlNeeded = true;
42+
if (lastCrawlFile.exists()) {
43+
BufferedReader br = new BufferedReader(new FileReader(lastCrawlFile));
44+
lastCrawl = br.readLine();
45+
br.close();
46+
if (lastCrawl != null && !lastCrawl.isEmpty()) {
47+
try {
48+
long crawlTime = java.text.DateFormat.getDateInstance().parse(lastCrawl).getTime();
49+
crawlNeeded = (System.currentTimeMillis() - crawlTime) > 30L * 24 * 60 * 60 * 1000;
50+
} catch (Exception ignored) {}
51+
}
52+
}
53+
%>
54+
<div class="table-wrap">
55+
<table>
56+
<thead><tr><th>Property</th><th>Value</th></tr></thead>
57+
<tbody>
58+
<tr><td>Last Crawl</td><td><%= lastCrawl %></td></tr>
59+
<tr><td>Crawl Needed</td><td style="color:<%= crawlNeeded ? "#ef4444" : "#22c55e" %>;"><%= crawlNeeded ? "Yes (>30 days)" : "No (within 30 days)" %></td></tr>
60+
<tr><td>Target</td><td><code>members.parliament.uk/member/{0..999}/contact</code></td></tr>
61+
<tr><td>Policy</td><td>Skip if .last-crawl &lt; 30 days old</td></tr>
62+
<tr><td>Force Re-crawl</td><td><code>rm modules/AE6E66/configuration/.last-crawl</code></td></tr>
63+
</tbody>
64+
</table>
65+
</div>
66+
<div style="margin-top:2rem;">
67+
<p style="font-size:0.85rem;color:var(--text-muted);">To trigger a crawl, run on the server:</p>
68+
<pre style="background:var(--bg-card);padding:1rem;border-radius:8px;margin-top:0.5rem;font-size:0.8rem;color:var(--text-secondary);overflow-x:auto;">java -cp modules/AE6E66/source source.AE6E66Main</pre>
69+
</div>
70+
</div>
71+
</section>
72+
73+
<footer class="footer"><div><span>&#169; 2026 MEARVK LLC. All rights reserved.</span></div></footer>
74+
</body>
75+
</html>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
:root {
2+
--bg-dark: #0a0f0a;
3+
--bg-section: #0f1a0f;
4+
--bg-card: #142814;
5+
--text-primary: #ffffff;
6+
--text-secondary: #a1b5a1;
7+
--text-muted: #5f7a5f;
8+
--accent: #22c55e;
9+
--accent-hover: #16a34a;
10+
--border: #1e3a1e;
11+
--font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
12+
}
13+
* { margin:0; padding:0; box-sizing:border-box; }
14+
body { font-family:var(--font); background:var(--bg-dark); color:var(--text-secondary); line-height:1.6; -webkit-font-smoothing:antialiased; }
15+
a { color:var(--accent); text-decoration:none; transition:color 0.2s; }
16+
a:hover { color:var(--accent-hover); }
17+
.nav { position:sticky; top:0; z-index:100; background:rgba(10,15,10,0.92); backdrop-filter:blur(12px); border-bottom:1px solid var(--border); }
18+
.nav-inner { max-width:1200px; margin:0 auto; padding:0 2rem; height:64px; display:flex; align-items:center; justify-content:space-between; }
19+
.nav-brand { font-size:1.125rem; font-weight:700; color:var(--text-primary); letter-spacing:-0.02em; }
20+
.nav-links { list-style:none; display:flex; gap:1.5rem; }
21+
.nav-links a { font-size:0.875rem; color:var(--text-secondary); }
22+
.nav-links a.active, .nav-links a:hover { color:var(--accent); }
23+
.nav-actions { display:flex; gap:0.75rem; }
24+
.nav-cta { font-size:0.8rem; padding:0.4rem 0.9rem; border:1px solid var(--border); border-radius:6px; color:var(--text-secondary); transition:all 0.2s; }
25+
.nav-cta:hover { border-color:var(--accent); color:var(--accent); }
26+
.hero { padding:6rem 2rem; text-align:center; }
27+
.hero-inner { max-width:800px; margin:0 auto; }
28+
.hero-tag { font-size:0.75rem; text-transform:uppercase; letter-spacing:0.05em; color:var(--accent); font-weight:600; }
29+
.hero h1 { font-size:clamp(2.5rem,5vw,3.75rem); font-weight:800; color:var(--text-primary); margin:0.5rem 0; letter-spacing:-0.03em; }
30+
.hero p { font-size:1.1rem; color:var(--text-secondary); max-width:600px; margin:0 auto; }
31+
.section { padding:5rem 2rem; border-top:1px solid var(--border); }
32+
.section-inner { max-width:1200px; margin:0 auto; }
33+
.section-inner h2 { font-size:1.75rem; font-weight:700; color:var(--text-primary); margin-bottom:1rem; }
34+
.table-wrap { overflow-x:auto; border:1px solid var(--border); border-radius:8px; }
35+
table { width:100%; border-collapse:collapse; font-size:0.875rem; }
36+
thead { background:var(--bg-card); }
37+
th { padding:0.75rem 1rem; text-align:left; font-size:0.75rem; text-transform:uppercase; letter-spacing:0.05em; color:var(--text-muted); font-weight:600; }
38+
td { padding:0.6rem 1rem; border-top:1px solid var(--border); color:var(--text-secondary); }
39+
tr:hover { background:rgba(34,197,94,0.04); }
40+
.footer { padding:3rem 2rem; border-top:1px solid var(--border); text-align:center; }
41+
.footer span { font-size:0.8rem; color:var(--text-muted); }
42+
.tabs { display:flex; gap:0.5rem; margin-bottom:1.5rem; flex-wrap:wrap; }
43+
.tab { padding:0.5rem 1rem; border:1px solid var(--border); border-radius:6px; font-size:0.8rem; color:var(--text-secondary); }
44+
.tab.active, .tab:hover { border-color:var(--accent); color:var(--accent); }
45+
code { background:var(--bg-card); padding:0.15rem 0.4rem; border-radius:4px; font-size:0.85em; }
948 KB
Loading

0 commit comments

Comments
 (0)