Quellcode durchsuchen

Add database layer and Sequelize models

k4be vor 7 Stunden
Ursprung
Commit
57eb88caf4

+ 36 - 0
gpx-vis-backend/src/database.js

@@ -0,0 +1,36 @@
+const { Sequelize } = require('sequelize');
+const path = require('path');
+const fs = require('fs');
+
+let config;
+try { config = require('../config'); } catch(e) { config = {}; }
+
+let sequelize;
+
+function getSequelize() {
+  if (sequelize) return sequelize;
+  const db = config.database || {};
+  if (db.type === 'sqlite' || !db.type) {
+    const storage = path.resolve(db.storage || './data/gpx-vis.db');
+    fs.mkdirSync(path.dirname(storage), { recursive: true });
+    sequelize = new Sequelize({ dialect: 'sqlite', storage, logging: false });
+  } else {
+    sequelize = new Sequelize(db.database, db.username, db.password, {
+      host: db.host || 'localhost',
+      port: db.port || 3306,
+      dialect: db.type,
+      logging: false,
+    });
+  }
+  return sequelize;
+}
+
+async function initDatabase() {
+  const sq = getSequelize();
+  // Import models to register them
+  require('./models');
+  await sq.sync({ alter: true });
+  console.log('Database initialized');
+}
+
+module.exports = { getSequelize, initDatabase };

+ 6 - 0
gpx-vis-backend/src/models/Directory.js

@@ -0,0 +1,6 @@
+module.exports = (sequelize, DataTypes) => sequelize.define('Directory', {
+  id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
+  userId: { type: DataTypes.INTEGER, allowNull: false },
+  name: { type: DataTypes.STRING(255), allowNull: false },
+  parentId: { type: DataTypes.INTEGER, allowNull: true },
+}, { tableName: 'directories', timestamps: true });

+ 5 - 0
gpx-vis-backend/src/models/ShareLink.js

@@ -0,0 +1,5 @@
+module.exports = (sequelize, DataTypes) => sequelize.define('ShareLink', {
+  id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
+  trackId: { type: DataTypes.INTEGER, allowNull: false, unique: true },
+  code: { type: DataTypes.STRING(20), allowNull: false, unique: true },
+}, { tableName: 'share_links', timestamps: true });

+ 11 - 0
gpx-vis-backend/src/models/Track.js

@@ -0,0 +1,11 @@
+module.exports = (sequelize, DataTypes) => sequelize.define('Track', {
+  id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
+  userId: { type: DataTypes.INTEGER, allowNull: false },
+  directoryId: { type: DataTypes.INTEGER, allowNull: true },
+  name: { type: DataTypes.STRING(255), allowNull: false },
+  originalFilename: { type: DataTypes.STRING(255) },
+  uploadDate: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
+  trackDate: { type: DataTypes.DATE, allowNull: true },
+  pointCount: { type: DataTypes.INTEGER, defaultValue: 0 },
+  totalDistance: { type: DataTypes.FLOAT, defaultValue: 0 }, // meters
+}, { tableName: 'tracks', timestamps: true });

+ 10 - 0
gpx-vis-backend/src/models/TrackPoint.js

@@ -0,0 +1,10 @@
+module.exports = (sequelize, DataTypes) => sequelize.define('TrackPoint', {
+  id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
+  trackId: { type: DataTypes.INTEGER, allowNull: false },
+  lat: { type: DataTypes.DOUBLE, allowNull: false },
+  lon: { type: DataTypes.DOUBLE, allowNull: false },
+  elevation: { type: DataTypes.FLOAT, allowNull: true },
+  time: { type: DataTypes.DATE, allowNull: true },
+  sequence: { type: DataTypes.INTEGER, allowNull: false },
+  segmentId: { type: DataTypes.INTEGER, defaultValue: 0 },
+}, { tableName: 'track_points', timestamps: false });

+ 8 - 0
gpx-vis-backend/src/models/User.js

@@ -0,0 +1,8 @@
+module.exports = (sequelize, DataTypes) => sequelize.define('User', {
+  id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
+  login: { type: DataTypes.STRING(64), allowNull: false, unique: true },
+  email: { type: DataTypes.STRING(255), allowNull: true },
+  passwordHash: { type: DataTypes.STRING(255), allowNull: false },
+  isAdmin: { type: DataTypes.BOOLEAN, defaultValue: false },
+  isActive: { type: DataTypes.BOOLEAN, defaultValue: false },
+}, { tableName: 'users', timestamps: true });

+ 30 - 0
gpx-vis-backend/src/models/index.js

@@ -0,0 +1,30 @@
+const { getSequelize } = require('../database');
+const { DataTypes } = require('sequelize');
+
+const sequelize = getSequelize();
+
+const User = require('./User')(sequelize, DataTypes);
+const Directory = require('./Directory')(sequelize, DataTypes);
+const Track = require('./Track')(sequelize, DataTypes);
+const TrackPoint = require('./TrackPoint')(sequelize, DataTypes);
+const ShareLink = require('./ShareLink')(sequelize, DataTypes);
+
+// Associations
+User.hasMany(Directory, { foreignKey: 'userId', onDelete: 'CASCADE' });
+Directory.belongsTo(User, { foreignKey: 'userId' });
+
+User.hasMany(Track, { foreignKey: 'userId', onDelete: 'CASCADE' });
+Track.belongsTo(User, { foreignKey: 'userId' });
+
+Directory.hasMany(Directory, { as: 'children', foreignKey: 'parentId' });
+Directory.belongsTo(Directory, { as: 'parent', foreignKey: 'parentId' });
+Directory.hasMany(Track, { foreignKey: 'directoryId' });
+Track.belongsTo(Directory, { foreignKey: 'directoryId' });
+
+Track.hasMany(TrackPoint, { foreignKey: 'trackId', onDelete: 'CASCADE' });
+TrackPoint.belongsTo(Track, { foreignKey: 'trackId' });
+
+Track.hasOne(ShareLink, { foreignKey: 'trackId', onDelete: 'CASCADE' });
+ShareLink.belongsTo(Track, { foreignKey: 'trackId' });
+
+module.exports = { User, Directory, Track, TrackPoint, ShareLink };